From 530b9c987907bc2f78afafd65a2fb381978eb1f3 Mon Sep 17 00:00:00 2001 From: Marius Ciepluch <11855163+norandom@users.noreply.github.com> Date: Sun, 13 Sep 2026 17:08:49 +0200 Subject: [PATCH] feat(pre-push-safety-net): reuse compatible artifacts throughout replay --- .kiro/specs/pre-push-safety-net/brief.md | 20 +- .kiro/specs/pre-push-safety-net/design.md | 8 +- .../implementation-group-6.md | 93 + .kiro/specs/pre-push-safety-net/spec.json | 21 +- .kiro/specs/pre-push-safety-net/tasks.md | 20 +- .kiro/steering/roadmap.md | 21 +- .kiro/steering/safety-net.md | 13 +- .../measurements/2026-09-08-module-audit.json | 36 +- .../2026-09-13-reuse-control.json | 1627 +++++++++++++++++ .../2026-09-13-reuse-final-control.json.gz | Bin 0 -> 35497 bytes .../2026-09-13-reuse-initial-control.json.gz | Bin 0 -> 26835 bytes benchmarks/push/README.md | 21 + benchmarks/push/reuse_control.py | 133 ++ ops/README.md | 16 + src/openultrasast/cli.py | 11 +- src/openultrasast/cpg/backend.py | 49 +- src/openultrasast/push/reuse.py | 202 ++ src/openultrasast/push/runner.py | 97 +- tests/test_graph_reuse.py | 18 + tests/test_push_reuse.py | 89 + tests/test_push_runner.py | 81 + 21 files changed, 2522 insertions(+), 54 deletions(-) create mode 100644 benchmarks/measurements/2026-09-13-reuse-control.json create mode 100644 benchmarks/measurements/2026-09-13-reuse-final-control.json.gz create mode 100644 benchmarks/measurements/2026-09-13-reuse-initial-control.json.gz create mode 100644 benchmarks/push/reuse_control.py create mode 100644 src/openultrasast/push/reuse.py create mode 100644 tests/test_push_reuse.py diff --git a/.kiro/specs/pre-push-safety-net/brief.md b/.kiro/specs/pre-push-safety-net/brief.md index 67611a8..4bd08c5 100644 --- a/.kiro/specs/pre-push-safety-net/brief.md +++ b/.kiro/specs/pre-push-safety-net/brief.md @@ -1,6 +1,6 @@ # Brief: pre-push-safety-net -Status: requirements, design and tasks approved; groups 1–5 implemented and verified (16/27 subtasks). The first PMPro/Node experiment is measured NO-GO: six cold timeouts, no completed target checks. Groups 6–8 pending; normal capabilities remain disabled and warm hook latency unproven. Language: English. +Status: requirements, design and tasks approved; groups 1–6 implemented and verified (20/27 subtasks). Compatible preparation/graph/query reuse is demonstrated, including packaged JavaScript evidence equivalence. Groups 7–8 pending. Rollout remains NO-GO: changed-code feasibility and independent capability qualification are not established; normal capabilities remain disabled. Language: English. ## Problem @@ -10,12 +10,18 @@ maintainer explicitly rejects noisy advisory output as well as noisy blocking ou ## Current State -The scan driver and Joern arbiters exist, with demonstrated PHP/Python cases. Immutable push-tip -resolution, isolated source materialization and lexical change context are implemented and verified. -Ranker integration, persistent graph caching and an end-to-end deadline remain pending. -The driver builds a whole-root graph before cutting the region budget. The latest PMPro ranking -experiment alone spends about 130 seconds building and 296 seconds extracting evidence. A manually -selected two-file slice finding a CVE does not demonstrate automatic incremental analysis. +The existing ranker, Joern arbiters, immutable replay, change comparison, actionability policy +and absolute deadline are integrated. Vendor code is physically excluded from frontend graphs +and targets. Explicit local caching reuses complete discovery, validated isolated graph copies +and compatible query evidence while reapplying current admission. Changed source/declarations +invalidate affected graph units; no incremental Joern mutation is implemented. + +Group 5's PMPro/Node cold experiment remains NO-GO. Group 6 adds a packaged JavaScript +correctness control with equal cached/uncached evidence and admission; an identical-revision +hit is fast, while the changed-code deadline remains a separate unmet gate. Automatic +context projection and independent capability eligibility remain explicit limits. See +implementation-group-6.md and the committed reuse control evidence. Historical whole-PMPro +ranking costs and manually selected CVE slices do not establish automatic push latency. ## Desired Outcome diff --git a/.kiro/specs/pre-push-safety-net/design.md b/.kiro/specs/pre-push-safety-net/design.md index 2d1d04e..b7c9823 100644 --- a/.kiro/specs/pre-push-safety-net/design.md +++ b/.kiro/specs/pre-push-safety-net/design.md @@ -147,6 +147,7 @@ New files (proposed, not created by this documentation change): | `src/openultrasast/push/contracts.py` | Immutable ref/comparison identities and comparison-owned completion/status records | | `src/openultrasast/push/snapshot.py` | Parse ref updates, resolve bases, materialize blobs, produce change context | | `src/openultrasast/push/cache.py` | Artifact keys, validity, atomic publication and eviction | +| `src/openultrasast/push/reuse.py` | Complete discovery reuse and backend graph/query reuse through the existing driver | | `src/openultrasast/push/policy.py` | Delta identity, actionability admission and push disposition | | `src/openultrasast/push/runner.py` | Shared deadline, ranker/engine orchestration, per-ref result aggregation | | `src/openultrasast/push/report.py` | Compact terminal result and reproducible push manifest | @@ -157,8 +158,9 @@ Contract configuration (task 1.4) uses `[push]`: optional `comparison_base`, pos `deadline_seconds` (30), positive `cancellation_allowance_seconds` (2), positive integer `cache_max_bytes` (2 GiB), `mode` (`advisory` by default, or `blocking`) and independent `incomplete_coverage_policy` (`allow` by default, or `block`). Unknown keys, malformed -sections and coerced/nonfinite numeric values are rejected. These settings are inert until -runner integration; they do not enable a hook or admit experimental capabilities. The cache +sections and coerced/nonfinite numeric values are rejected. Experimental replay consumes the deadline, mode, coverage policy and optional cache budget. +An explicit `--cache-dir` enables local reuse; omission retains a cold control. These settings +do not install a hook or admit experimental capabilities. The cache limit is a local storage budget, not a latency claim. Completion records are owned by an exact comparison so reused head graphs cannot merge evidence from different bases. Live leases have no JSON constructor; only the backend can validate and issue them in task 6.1. @@ -251,7 +253,7 @@ cleanup consume the same execution deadline and cancellation allowance as the ba Runtime origin is `unspecified` when no declaration establishes browser/server scope; JavaScript presence alone does not admit an Express capability. TypeScript and C/C++ retain explicit capability limits. These records prepare later artifact manifests; -they do not implement persistent graph storage. +task 6 now binds them to backend-owned persistent graph manifests and isolated leases. Graph key: digest of every included blob/path and applicable declarations, frontend/engine version, overlay semantics/options, language, and exclusions. Query key additionally includes graph key, diff --git a/.kiro/specs/pre-push-safety-net/implementation-group-6.md b/.kiro/specs/pre-push-safety-net/implementation-group-6.md index c8c0a3a..b5ca26c 100644 --- a/.kiro/specs/pre-push-safety-net/implementation-group-6.md +++ b/.kiro/specs/pre-push-safety-net/implementation-group-6.md @@ -103,3 +103,96 @@ the cache never confers eligibility. JSON decoding is non-executable and bounded - CLAIM: Task 6.3 semantic keys and completed comparison evidence reuse. - EVIDENCE: production policy integration and cache key tests, 1260 passing suite tests and final static checks. - GAPS: Graph/query/preparation reuse must still be connected to replay under 6.4; no rollout claim. + +## Task 6.4 brief + +Requirements 1.1, 2.2, 4.1, 4.2, 4.3, 4.4 and 7.3; design: Push Runner and +Cache and Engine Boundary. Explicit `--cache-dir` integrates the three cache layers; +omitting it preserves a cold replay control. Complete immutable snapshots are still read +and verified by the adapter before persisted discovery can be used. Equal base/head revisions +share one live materialization. Discovery keys include exact file identities and shipped code/facts; +dependency/declaration changes invalidate them. Graph units remain the existing physical frontend +partitions. Source changes rebuild the affected unit; all declaration and exclusion context is +bound. There is no incremental graph mutation or changed-file scope selector. + +The runner uses backend-issued copies and per-question cached raw answers, then runs the existing +ranker, arbiter, comparison and current admission. Partial answers are not promoted to complete +lookups. Complete answers already returned by the engine remain available when persistence cannot +finish. Query keys include actual parameters and graph bytes; graph keys do not include unrelated +ranking/security-fact versions. Layout/exclusion facts and frontend adaptation bytes do affect them. +Integration review repaired the task 6.1 identity to include installed frontend/adaptation bytes, +rather than relying only on Joern's release label. This is an owning-contract repair, not a new +frontend or a detector change. + +The first source-mounted/package integration trial is retained separately from the final +frozen image run. It showed equal cached/uncached evidence and admission (including one +new JavaScript injection candidate), a 1.23-second identical-revision hit, and a 30.48-second +dependency-edit timeout. Those numbers are preliminary control evidence, not the final image +or rollout measurements. Final review also binds installed runtime identity into admission +semantics, while avoiding a second backend instance or an unbudgeted availability probe. + +The cache entry lease ends after its validated bytes are copied into the backend's independent +working lease. The working copy remains valid if storage later evicts the original entry; +queries never operate on the stored original. Partial fresh answers cannot borrow a cached +answer's census. Pure discovery remains in the existing bounded child process; persisted +JSON is decoded as data, never executable objects. + +## Status Report +- STATUS: READY_FOR_REVIEW +- TASK: 6.4 +- RED_PHASE_OUTPUT: `/tmp/group6-4-red.log`: graph/query reuse and replay discovery/cache-location tests failed before integration (temporary switch OFF). Enabled implementation passed; switch removed. Additional regressions cover mixed cached/fresh census failure and installed runtime provenance. +- TESTS_RUN: 89 final focused tests passed; full suite 1268 passed, 9 skipped in 108.74s. Ruff, formatting, mypy (114 source files), compileall, detection/map/pair gates and Docker build/packaged CLI smoke passed. +- EVIDENCE: final packaged JavaScript control exited 0 with exact cached/uncached scope, question outcomes, findings and admission equality. One new injection is retained as diagnostic evidence and denied normal admission by the empty registry. Source/dependency edits invalidate graph reuse and retain timed-out coverage artifacts. +- CONCERNS: identical-revision hits are development correctness controls, not representative warm p95 or capability qualification. Main-context review fallback remains explicit. + +## Review Verdict +- VERDICT: APPROVED +- TASK: 6.4 +- MECHANICAL_RESULTS: canonical tests and regression gates passed; final image reached the usable CLI and completed real replay controls. No introduced placeholders, secrets or boundary violations found. +- FINDINGS: installation-byte identity now also binds comparison/admission provenance. The predecessor no-unbudgeted-probe regression caught an unnecessary backend instance in provenance; the repair uses a pure runtime-identity function and the final regression passes. Mixed fresh/cached answers require the fresh query's own valid census before being retained as complete. Snapshot reads, discovery, ranker/driver selection, vendor boundary and independent push/coverage status remain intact. +- SUMMARY: Task 6.4 integrates valid local reuse and truthful misses under the existing transaction deadline without changing detection or admission rules. + +## Verification Result +- STATUS: VERIFIED +- CLAIM_TYPE: TASK +- CLAIM: Group 6 implementation (6.1–6.4), with compatible replay evidence and admission equivalence. +- EVIDENCE: final tests/static checks, native packaged Joern controls, and committed measurement record below. +- GAPS: Rollout remains NO-GO. Groups 7–8, representative changed-code completion/p95 and independent capability qualification remain pending. Automatic projection limitations are unchanged. + +## Final packaged evidence + +Image: `sha256:6622b846996989a321d81bfa6c25e792e3f217984d7ede66f830cff1206a634c`. +Nonroot, network disabled, 3 GiB memory; controls ran serially without concurrent +tests/builds. Filesystem cache was not flushed. Each case is one development control, +not a representative p95 population. + +| Run | Full CLI seconds | Graph hits | Query-answer hits | +|---|---:|---:|---:| +| populate | 154.848 | 0 | 0 | +| warm-identical | 2.002 | 2 | 37 | +| uncached | 130.432 | 0 | 0 | +| dependency-edit | 30.458 | 0 | 0 | +| one-function-edit | 30.479 | 0 | 0 | +| uncached, same revisions, 30s budget | 30.606 | 0 | 0 | + +Cached and uncached laboratory runs have **equal scope, question outcomes, findings and +admission**, including the known introduced JavaScript injection. The 30-second identical +cached run retains that same evidence: one completed supported head injection check plus +four base comparison checks. The same revisions without caching complete zero checks in +the 30-second control. All six CLI artifacts were retained, including timed-out edits. +Warm source/dependency edits correctly invalidate the affected graph and produce incomplete +coverage; they do not replay the old answer. The empty normal capability registry denies the +new diagnostic candidate for `capability_unavailable`; this is not a clean repository verdict. + +The manifest, full artifacts and stdout/stderr are retained in +`benchmarks/measurements/2026-09-13-reuse-control.json` and the associated final/initial +compressed bundles. Each bundle maps original filenames to exact UTF-8 content; decompression +was compared with the original files and SHA-256 hashes recorded. The committed reusable +harness is `benchmarks/push/reuse_control.py`; the extra same-revision 30-second uncached +command is recorded in the final bundle. + +**Group 6 implementation: verified. Rollout: NO-GO.** Changed-code feasibility remains +unmet in these controls, and independent precision/recall/capability populations have not +qualified. The spec now explicitly requires stage-level investigation of remaining timeouts +under task 8.2, retaining its original thresholds and population rules. Group 7 is next; +no hook has been installed and no normal capability enabled. diff --git a/.kiro/specs/pre-push-safety-net/spec.json b/.kiro/specs/pre-push-safety-net/spec.json index cd3c6ea..931f8ce 100644 --- a/.kiro/specs/pre-push-safety-net/spec.json +++ b/.kiro/specs/pre-push-safety-net/spec.json @@ -1,7 +1,7 @@ { "feature_name": "pre-push-safety-net", "created_at": "2026-09-12T15:35:00Z", - "updated_at": "2026-09-13T13:22:55.634332+00:00", + "updated_at": "2026-09-13T15:06:56.856952+00:00", "language": "en", "phase": "implementation", "approvals": { @@ -19,7 +19,7 @@ } }, "ready_for_implementation": true, - "implementation_approval_note": "Groups1\u20135 implemented and verified (16/27). Group5 used manual Kiro fallback review, repaired observed predecessor deadline bugs, and executed a newly frozen PMPro/Node experiment. Measured feasibility NO-GO; real capabilities remain disabled. Groups6\u20138 pending.", + "implementation_approval_note": "Groups 1\u20136 implemented and verified (20/27); group 6 authorized as a whole. Main-context Kiro review fallback after fresh dispatch thread limit. Valid reuse is demonstrated; real capability registry remains empty and rollout gates remain unmet. Groups 7\u20138 pending.", "depends_on": [ "contributor-scan" ], @@ -98,19 +98,24 @@ "4.2", "4.3", "5.1", - "5.2" + "5.2", + "6.1", + "6.2", + "6.3", + "6.4" ], - "remaining_tasks": 11, - "verification_record": "implementation-group-5.md", + "remaining_tasks": 7, + "verification_record": "implementation-group-6.md", "verification_records": [ "implementation-group-1.md", "implementation-group-2.md", "implementation-group-3.md", "implementation-group-4.md", - "implementation-group-5.md" + "implementation-group-5.md", + "implementation-group-6.md" ], - "next_group": 6, + "next_group": 7, "blocked_tasks": [], - "feasibility": "NO-GO: six cold replay timeouts; no completed supported target checks; warm performance and eligibility unproven" + "feasibility": "NO-GO for rollout: compatible identical-revision reuse verified; representative changed-code p95/completion and independent alert eligibility remain unmet." } } diff --git a/.kiro/specs/pre-push-safety-net/tasks.md b/.kiro/specs/pre-push-safety-net/tasks.md index a624617..9b77162 100644 --- a/.kiro/specs/pre-push-safety-net/tasks.md +++ b/.kiro/specs/pre-push-safety-net/tasks.md @@ -1,11 +1,12 @@ # Implementation Plan: pre-push-safety-net -Status: task plan approved by the maintainer on 2026-09-12. Groups 1–5 (tasks 1.1–5.2) -are implemented, reviewed and verified (16/27 subtasks). Tasks 4.3 and group 5 used the -documented Kiro manual review fallback after agent dispatch reached its thread limit. -The first experiment is measured NO-GO: all six cold PMPro/Node cases timed out without -completed target checks. Groups 6–8 remain pending; no production capability or warm -hook latency is admitted. +Status: task plan approved by the maintainer on 2026-09-12. Groups 1–6 (tasks 1.1–6.4) +are implemented, reviewed and verified (20/27 subtasks). Tasks 4.3 and groups 5–6 used the +explicit Kiro manual review fallback after fresh agent dispatch reached its thread limit. +Group 5's six cold PMPro/Node timeouts remain historical NO-GO evidence. Group 6 verifies +compatible reuse with equal evidence/admission in a packaged authored JavaScript control; +its identical-revision run completed in 2.00s. This is not representative warm p95 or +independent capability qualification. Groups 7–8 remain pending; rollout remains NO-GO. ## Execution contract @@ -160,7 +161,7 @@ hook latency is admitted. - _Depends: 1.2, 1.3, 5.1_ - _Requirements: 6.2, 6.3, 6.4, 6.5, 8.1, 8.2, 8.3, 8.4_ -- [ ] 6. Reuse valid graphs and results without stale conclusions +- [x] 6. Reuse valid graphs and results without stale conclusions - [x] 6.1 Validate graph artifact identity and leases - Have the backend own source/declaration/exclusion digests, frontend/engine/overlay options, graph census and complete/partial identity. - Permit validated graph leases in the generic driver while preserving the fresh-build path; isolate potentially mutating loads and retain bounded cleanup. @@ -180,7 +181,7 @@ hook latency is admitted. - Done when source, context, fact, query, mode, policy and optional model changes invalidate the appropriate layer, while an identical compatible request returns the same evidence and provenance. - _Boundary: Push cache, policy integration_ - _Requirements: 3.4, 4.3, 4.4, 6.4, 7.2, 7.3_ -- [ ] 6.4 Integrate cache reuse into the shared push deadline +- [x] 6.4 Integrate cache reuse into the shared push deadline - Reuse complete preparation/discovery by immutable tree and discovery/fact/configuration identity, retaining readable snapshot and declaration/boundary records; measure its costs and hits separately from graph/query reuse. - Reuse identical snapshots/unchanged declared units across refs without merging different comparisons; changed units rebuild and stale graph relationships remain hints only. - Account for lookup, lock wait, lease, build/query and result persistence in the same deadline; preserve completed outcomes on cancellation. @@ -219,6 +220,7 @@ hook latency is admitted. - _Requirements: 1.1, 1.2, 1.3, 1.4, 2.2, 2.3, 4.2, 4.4, 5.2, 5.5, 7.1, 7.4, 8.2_ - [ ] 8.2 Measure realistic cache and deadline feasibility - Measure identical-tip reuse, a warm one-function edit, dependency/configuration edits, cold starts, repository growth and multi-ref workloads on declared hardware. + - If reuse correctness passes but changed-code checks time out, identify preparation, frontend/overlay/census and query costs separately and route the remaining optimization to its owning boundary before rollout GO. Preserve the declared gates and do not substitute identical-tip latency. - Publish p50/p95, timeouts, cancellation allowance, completed/deferred population and stage costs; the denominator is fixed before ranker selection. - Done when the versioned scorecard evaluates the accepted latency/coverage gates without hiding timeouts or calling repeated-identical-tip hits representative warm edits; a failed gate remains NO-GO for rollout. - _Boundary: Evaluation harness, runtime integration_ @@ -244,3 +246,5 @@ hook latency is admitted. - Task5.2 measured PMPro timing out after both snapshots were materialized but before change context/graph analysis. Group6 must account for compatible snapshot/discovery reuse as well as graph reuse; identical-tip hits cannot substitute for the later representative changed-code latency gate. No new detector, scope heuristic or incremental CPG mutation is implied. - Cancellation must cover semantic planning and reporting, not only engine subprocesses: `_collect` and `_scope_work` now consume the shared deadline, and replay instantiates the budget-aware backend once. Preserve the 400-question failure-census and real Node artifact-retention regressions. + +- Group 6: complete discovery/graph/query reuse is explicit via `--cache-dir`; source/declaration edits invalidate affected units. Installed frontend/adaptation bytes now bind graph and admission provenance. A valid cached census cannot qualify a fresh answer that omitted its own census. Identical-revision timing is not the changed-code rollout gate. diff --git a/.kiro/steering/roadmap.md b/.kiro/steering/roadmap.md index f4d4c10..4fe9bd5 100644 --- a/.kiro/steering/roadmap.md +++ b/.kiro/steering/roadmap.md @@ -46,7 +46,7 @@ engine are out of scope. - [ ] pre-push-safety-net — Immutable pushed tips, existing-ranker integration, compatible artifact reuse, total deadline, change attribution, actionability admission and compact hook behavior. Dependencies: contributor-scan. Requirements/design approved 2026-09-12; tasks generated - (8 groups, 27 executable subtasks), independently reviewed and approved; groups 1–5 are implemented and verified (16/27 tasks), including the resolved frontend-retention prerequisite. + (8 groups, 27 executable subtasks), independently reviewed and approved; groups 1–6 are implemented and verified (20/27 tasks), including the resolved frontend-retention prerequisite. ### Next concrete step @@ -59,14 +59,19 @@ its deadline in preparation/discovery after materializing both trees; Node reach driver but did not complete checks. The exact 11-case population remains unresolved. No normal capability is enabled and these six cold samples establish no warm p95 claim. -Group 6 is next for valid graph/result identity, leases and reuse. Account for snapshot -and discovery costs before treating graph persistence as the sole optimization; record -compatible preparation reuse alongside graph reuse. Representative changed-code warm -latency and coverage must still be measured later, without substituting identical-tip hits. -Do not change ranking weights, add a second source-slicer or assume incremental Joern -mutation from this negative performance evidence. +Group 6 now provides complete preparation/discovery, graph and query reuse through explicit +`--cache-dir`, bounded publication and isolated graph leases. A packaged authored JavaScript +control preserved the same evidence/admission as an uncached run; its identical-revision +replay took 2.00s. Source/declaration changes invalidate affected units. This correctness +control does not establish representative changed-code p95, independent alert precision or +eligibility. Rollout remains NO-GO and normal capabilities remain disabled. -Tasks 4.3 and group 5 used the documented Kiro manual review fallback after the agent +Group 7 is next for the real multi-ref push interface and opt-in hook integration. Group 8 +must still pass the joint gates on declared populations. Keep the existing thresholds and +profile preparation, graph construction and queries separately when investigating misses; +do not substitute identical-tip hits for warm changed-code performance. + +Tasks 4.3 and groups 5–6 used the documented Kiro manual review fallback after the agent thread limit; this is not independent review. See `pre-push-safety-net/implementation-group-5.md` and `benchmarks/measurements/2026-09-13-first-replay-experiment.json` for provenance, initial instrument failures, their repairs and the newly frozen final trial. The NodeGoat diff --git a/.kiro/steering/safety-net.md b/.kiro/steering/safety-net.md index b329613..3b3ada5 100644 --- a/.kiro/steering/safety-net.md +++ b/.kiro/steering/safety-net.md @@ -79,16 +79,21 @@ updates as a shortcut to an unmeasured latency promise. The product direction above is user-requested. The amended requirements, design and initial evaluation profile in `pre-push-safety-net` were approved for task generation on 2026-09-12 ("ok, next kiro phase"). The maintainer then approved the task plan and starting group 1 -("approve.") and then group 2 ("ok, next kiro phase, approved"). Groups 1–5 are now -implemented, reviewed and verified (16/27), including the frontend-retention prerequisite -and the deadline regression exposed by full Node replay. Tasks 4.3 and group 5 used the +("approve.") and then group 2 ("ok, next kiro phase, approved"). Groups 1–6 are now +implemented, reviewed and verified (20/27), including the frontend-retention prerequisite +and the deadline regression exposed by full Node replay. Tasks 4.3 and groups 5–6 used the explicit Kiro manual review fallback after the agent thread limit. The first newly frozen PMPro/Node experiment is measured **NO-GO**: all six cold cases timed out without completed supported target checks, while retaining all 11 declared cases. The repaired CLI saved all artifacts and returned in 30.55–30.64 seconds. PMPro exhausted the deadline in preparation/discovery before graph analysis, so compatible preparation -reuse matters alongside graph persistence. Group 6 is next; warm changed-code performance +reuse matters alongside graph persistence. Group 6 now provides compatible local reuse; warm changed-code performance and useful coverage remain mandatory later gates. No ranking/fact adaptation or holdout inspection occurred in the final transfer run. Real capability eligibility and automatic dominance context projection remain unproven; the default capability registry is empty. + +Group 6 verified equal cached/uncached evidence and current admission in a packaged authored +JavaScript control. Source/declaration edits invalidate affected units. Identical-revision +latency does not qualify representative changed-code performance. Requirements and rollout +thresholds remain unchanged; groups 7–8 and independent capability admission remain pending. diff --git a/benchmarks/measurements/2026-09-08-module-audit.json b/benchmarks/measurements/2026-09-08-module-audit.json index a60b336..b67dad7 100644 --- a/benchmarks/measurements/2026-09-08-module-audit.json +++ b/benchmarks/measurements/2026-09-08-module-audit.json @@ -1,10 +1,10 @@ { "counts": { - "load_bearing": 103, + "load_bearing": 104, "standalone_capability": 9 }, - "modules": 112, - "note": "Refreshed for task 6.2: bounded local artifact storage, integrated by task 6.4.", + "modules": 113, + "note": "Refreshed for group 6: replay reaches compatible graph, query, discovery and comparison reuse.", "orphaned": [], "rows": [ { @@ -118,6 +118,7 @@ "config", "cpg.artifact", "model.contracts", + "push.cache", "push.contracts", "push.policy" ], @@ -139,6 +140,7 @@ "model.taint", "push.cache", "push.policy", + "push.reuse", "push.runner" ], "module": "cpg", @@ -148,7 +150,10 @@ "classification": "load_bearing", "importers": [ "cpg.backend", - "push.cache" + "push.cache", + "push.policy", + "push.reuse", + "push.runner" ], "module": "cpg.artifact", "reason": "pre-push-safety-net Req 4.3/7.3: backend graph identity and live lease contract; validation integration pending" @@ -167,6 +172,7 @@ "model.taint", "push.cache", "push.policy", + "push.reuse", "push.runner" ], "module": "cpg.backend", @@ -374,6 +380,7 @@ "push.contracts", "push.policy", "push.report", + "push.reuse", "push.runner", "push.snapshot" ], @@ -421,6 +428,7 @@ "push.contracts", "push.policy", "push.report", + "push.reuse", "push.runner", "push.snapshot" ], @@ -484,7 +492,8 @@ "importers": [ "model.partitions", "model.scan", - "preprocess" + "preprocess", + "push.runner" ], "module": "model.layout", "reason": "reachable from gate, map_gate, pair_gate or the CLI" @@ -513,6 +522,7 @@ "model.layout", "model.scan", "push.policy", + "push.reuse", "push.runner" ], "module": "model.regions", @@ -690,6 +700,7 @@ "cli", "push.policy", "push.report", + "push.reuse", "push.runner", "push.snapshot" ], @@ -698,7 +709,11 @@ }, { "classification": "load_bearing", - "importers": [], + "importers": [ + "push.policy", + "push.reuse", + "push.runner" + ], "module": "push.cache", "reason": "pre-push-safety-net task 6.2: bounded artifact publication and leased eviction; runner integration in 6.4" }, @@ -707,6 +722,7 @@ "importers": [ "push.policy", "push.report", + "push.reuse", "push.runner", "push.snapshot" ], @@ -730,6 +746,14 @@ "module": "push.report", "reason": "pre-push-safety-net Req 5.1/5.2/5.5/7.3: compact output and complete artifacts; runner integration pending" }, + { + "classification": "load_bearing", + "importers": [ + "push.runner" + ], + "module": "push.reuse", + "reason": "reachable from gate, map_gate, pair_gate or the CLI" + }, { "classification": "load_bearing", "importers": [ diff --git a/benchmarks/measurements/2026-09-13-reuse-control.json b/benchmarks/measurements/2026-09-13-reuse-control.json new file mode 100644 index 0000000..e2e9ccc --- /dev/null +++ b/benchmarks/measurements/2026-09-13-reuse-control.json @@ -0,0 +1,1627 @@ +{ + "purpose": "Group 6 authored JavaScript reuse correctness and invalidation control; not independent capability qualification", + "image": "sha256:6622b846996989a321d81bfa6c25e792e3f217984d7ede66f830cff1206a634c", + "environment": { + "network": "none", + "memory_limit_bytes": 3221225472, + "user": "image nonroot ousast", + "filesystem_cache": "uncontrolled", + "workloads": "serial; no concurrent tests/builds during final timed controls" + }, + "harness": "benchmarks/push/reuse_control.py mounted read-only; additional same-revision 30-second uncached CLI command retained in bundle", + "harness_sha256": "900393eb6cc614162136f5a70b6be13fa34cff2e3a80824e69d8a4ccf8c77cf2", + "final_summary": { + "cold_populated_evidence_equal": true, + "cold_warm_evidence_equal": true, + "admission_equal": true, + "runs": [ + { + "name": "populate", + "elapsed_seconds": 154.84820935002062, + "exit_code": 0, + "timings": { + "deadline_seconds": 600.0, + "cancellation_allowance_seconds": 2.0, + "resolution_seconds": 0.01658846205100417, + "provenance_seconds": 0.3594230430899188, + "snapshot_seconds": 0.016923974035307765, + "snapshot_reuses": 0, + "discovery_seconds": 0.15945994993671775, + "discovery_hits": 0, + "preparation_seconds": 0.20162996603175998, + "head_seconds": 78.34064441197552, + "base_seconds": 75.3250151189277, + "admission_seconds": 0.006018119980581105, + "cleanup_seconds": 0.0003920870367437601, + "graph_hits": 0, + "query_hits": 0, + "total_seconds": 154.2775415830547 + }, + "result": { + "analyses": [ + { + "comparison": { + "head_oid": "458817181ffff46da686825cdea0e27189fb447d", + "base_oid": "54b3130ca5354201a00bbd841640f6cb8651aaf8", + "base_reason": "explicit_local_replay", + "refs": [ + "refs/heads/replay" + ] + }, + "scopes": [ + { + "ranker_version": "existing-static-evidence-v1", + "ranking_mode": "evidence", + "population_complete": true, + "selected": [ + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "access_control" + }, + "priority": 0.0, + "evidence": [ + "static_rank=1.0", + "taint_evidence_not_applicable", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ], + "tier": null, + "score": null + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "config_secrets" + }, + "priority": -1.0, + "evidence": [ + "static_rank=1.0", + "taint_evidence_not_applicable", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ], + "tier": null, + "score": null + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "injection" + }, + "priority": -2.0, + "evidence": [ + "static_rank=1.0", + "normalized_evidence={\"access_declared_public\":true,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":true,\"family_in_repo\":true,\"shipped\":true,\"sinks\":[{\"arg0_literal\":false,\"arity\":1,\"cleansed_on_call\":false,\"code\":\"eval(value)\",\"line\":3,\"method\":\"handler\"}],\"source_local\":true,\"source_near\":true}", + "tier=4", + "score=5.5", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "affected_relationship={\"evidence\":[\"base_span:api.js:2-2\",\"head_span:api.js:2-2\",\"unchanged_line:1;lexical_only\",\"unchanged_line:3;lexical_only\",\"unchanged_line:4;lexical_only\"],\"kind\":\"entry\",\"source\":{\"family\":\"injection\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"},\"target\":{\"family\":\"injection\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"}}" + ], + "tier": 4, + "score": 5.5 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "output_encoding" + }, + "priority": -3.0, + "evidence": [ + "static_rank=1.0", + "normalized_evidence={\"access_declared_public\":true,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":true,\"family_in_repo\":false,\"shipped\":true,\"sinks\":[],\"source_local\":true,\"source_near\":true}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "affected_relationship={\"evidence\":[\"base_span:api.js:2-2\",\"head_span:api.js:2-2\",\"unchanged_line:1;lexical_only\",\"unchanged_line:3;lexical_only\",\"unchanged_line:4;lexical_only\"],\"kind\":\"entry\",\"source\":{\"family\":\"output_encoding\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"},\"target\":{\"family\":\"output_encoding\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"}}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "path" + }, + "priority": -4.0, + "evidence": [ + "static_rank=1.0", + "normalized_evidence={\"access_declared_public\":true,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":true,\"family_in_repo\":false,\"shipped\":true,\"sinks\":[],\"source_local\":true,\"source_near\":true}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "affected_relationship={\"evidence\":[\"base_span:api.js:2-2\",\"head_span:api.js:2-2\",\"unchanged_line:1;lexical_only\",\"unchanged_line:3;lexical_only\",\"unchanged_line:4;lexical_only\"],\"kind\":\"entry\",\"source\":{\"family\":\"path\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"},\"target\":{\"family\":\"path\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"}}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "prototype" + }, + "priority": -5.0, + "evidence": [ + "static_rank=1.0", + "normalized_evidence={\"access_declared_public\":true,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":true,\"family_in_repo\":false,\"shipped\":true,\"sinks\":[],\"source_local\":true,\"source_near\":true}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "affected_relationship={\"evidence\":[\"base_span:api.js:2-2\",\"head_span:api.js:2-2\",\"unchanged_line:1;lexical_only\",\"unchanged_line:3;lexical_only\",\"unchanged_line:4;lexical_only\"],\"kind\":\"entry\",\"source\":{\"family\":\"prototype\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"},\"target\":{\"family\":\"prototype\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"}}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "untrusted_destination" + }, + "priority": -6.0, + "evidence": [ + "static_rank=1.0", + "normalized_evidence={\"access_declared_public\":true,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":true,\"family_in_repo\":false,\"shipped\":true,\"sinks\":[],\"source_local\":true,\"source_near\":true}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "affected_relationship={\"evidence\":[\"base_span:api.js:2-2\",\"head_span:api.js:2-2\",\"unchanged_line:1;lexical_only\",\"unchanged_line:3;lexical_only\",\"unchanged_line:4;lexical_only\"],\"kind\":\"entry\",\"source\":{\"family\":\"untrusted_destination\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"},\"target\":{\"family\":\"untrusted_destination\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"}}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "config_secrets" + }, + "priority": -7.0, + "evidence": [ + "static_rank=0.15", + "taint_evidence_not_applicable", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ], + "tier": null, + "score": null + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "injection" + }, + "priority": -8.0, + "evidence": [ + "static_rank=0.15", + "normalized_evidence={\"access_declared_public\":false,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":false,\"family_in_repo\":true,\"shipped\":true,\"sinks\":[],\"source_local\":false,\"source_near\":false}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "output_encoding" + }, + "priority": -9.0, + "evidence": [ + "static_rank=0.15", + "normalized_evidence={\"access_declared_public\":false,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":false,\"family_in_repo\":false,\"shipped\":true,\"sinks\":[],\"source_local\":false,\"source_near\":false}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "path" + }, + "priority": -10.0, + "evidence": [ + "static_rank=0.15", + "normalized_evidence={\"access_declared_public\":false,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":false,\"family_in_repo\":false,\"shipped\":true,\"sinks\":[],\"source_local\":false,\"source_near\":false}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "prototype" + }, + "priority": -11.0, + "evidence": [ + "static_rank=0.15", + "normalized_evidence={\"access_declared_public\":false,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":false,\"family_in_repo\":false,\"shipped\":true,\"sinks\":[],\"source_local\":false,\"source_near\":false}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "untrusted_destination" + }, + "priority": -12.0, + "evidence": [ + "static_rank=0.15", + "normalized_evidence={\"access_declared_public\":false,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":false,\"family_in_repo\":false,\"shipped\":true,\"sinks\":[],\"source_local\":false,\"source_near\":false}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + } + ], + "deferred": [], + "unresolved_boundaries": [ + "context_projection_unavailable:contributor-scan:7ad5ff7dd4882f08a395c2c0f55de6f6a3ddb9f9db1536b207572a65485776ac", + "context_projection_unavailable:contributor-scan:010672f395d61fff5ca6acb6e5fb3e3aae4dcf9c30887240b985e705b656717a", + "dynamic_external_or_depth_context_unresolved:contributor-scan:e3453476e53cbccfd684460af10a161bb890fc026f3d10e18b30405d62a3d6cd", + "dynamic_external_or_depth_context_unresolved:contributor-scan:7a8bdefd0721637c5f48e97e26439ce8b329b293d8f6e65367e9e0c362f27b1d", + "dynamic_external_or_depth_context_unresolved:contributor-scan:1f94e3ba635eccd3f41986a6d98fc72c3f80358fe857e753b320e35cf6b73d64", + "dynamic_external_or_depth_context_unresolved:contributor-scan:3e5e7ffa9d58f7410260699db0971f16db0f439393765944fe1fb80eadda97e8", + "context_projection_unavailable:contributor-scan:1354f0d585180f0836d0688fa02f6e277e23082a8586e943e6d14a6b6c674937", + "context_scope_empty:contributor-scan:a707b5a8dd0057e55f54f4a8920d895cebf04fa14b9a7464a40ac60b9ba5002f", + "context_scope_empty:contributor-scan:915e4a4f1f95ecd70094688f73feb49a8be41d7a7a151d8e35f2d42035cecdf0", + "context_scope_empty:contributor-scan:f73e64bc02e13221cf6c8e8ae697e1ba2857957157a9b2fd216e556bd8d57a1a", + "context_scope_empty:contributor-scan:66f58a5dc208fab2d2634273b2f4b4048e593b4933ccfb1093baf39c04027ab5", + "context_scope_empty:contributor-scan:1c1ec68dedd385fa5c98bd93ae642d8384a6fa384470cd8290623842d921bfa2" + ] + } + ], + "completed_questions": [ + { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "injection" + } + ], + "coverage_status": "incomplete" + } + ], + "finding_status": "none", + "coverage_status": "incomplete", + "push_disposition": "allow", + "actionable_defect_ids": [] + }, + "artifact": "/results/populate.json" + }, + { + "name": "warm-identical", + "elapsed_seconds": 2.002456544083543, + "exit_code": 0, + "timings": { + "deadline_seconds": 30.0, + "cancellation_allowance_seconds": 2.0, + "resolution_seconds": 0.01683276705443859, + "provenance_seconds": 0.3089857939630747, + "snapshot_seconds": 0.0196256039198488, + "snapshot_reuses": 0, + "discovery_seconds": 0.010183473001234233, + "discovery_hits": 2, + "preparation_seconds": 0.05007663299329579, + "head_seconds": 0.5239870100049302, + "base_seconds": 0.5983346940483898, + "admission_seconds": 0.0056962770177051425, + "cleanup_seconds": 0.00035001395735889673, + "graph_hits": 2, + "query_hits": 37, + "total_seconds": 1.5298795499838889 + }, + "result": { + "analyses": [ + { + "comparison": { + "head_oid": "458817181ffff46da686825cdea0e27189fb447d", + "base_oid": "54b3130ca5354201a00bbd841640f6cb8651aaf8", + "base_reason": "explicit_local_replay", + "refs": [ + "refs/heads/replay" + ] + }, + "scopes": [ + { + "ranker_version": "existing-static-evidence-v1", + "ranking_mode": "evidence", + "population_complete": true, + "selected": [ + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "access_control" + }, + "priority": 0.0, + "evidence": [ + "static_rank=1.0", + "taint_evidence_not_applicable", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ], + "tier": null, + "score": null + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "config_secrets" + }, + "priority": -1.0, + "evidence": [ + "static_rank=1.0", + "taint_evidence_not_applicable", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ], + "tier": null, + "score": null + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "injection" + }, + "priority": -2.0, + "evidence": [ + "static_rank=1.0", + "normalized_evidence={\"access_declared_public\":true,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":true,\"family_in_repo\":true,\"shipped\":true,\"sinks\":[{\"arg0_literal\":false,\"arity\":1,\"cleansed_on_call\":false,\"code\":\"eval(value)\",\"line\":3,\"method\":\"handler\"}],\"source_local\":true,\"source_near\":true}", + "tier=4", + "score=5.5", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "affected_relationship={\"evidence\":[\"base_span:api.js:2-2\",\"head_span:api.js:2-2\",\"unchanged_line:1;lexical_only\",\"unchanged_line:3;lexical_only\",\"unchanged_line:4;lexical_only\"],\"kind\":\"entry\",\"source\":{\"family\":\"injection\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"},\"target\":{\"family\":\"injection\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"}}" + ], + "tier": 4, + "score": 5.5 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "output_encoding" + }, + "priority": -3.0, + "evidence": [ + "static_rank=1.0", + "normalized_evidence={\"access_declared_public\":true,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":true,\"family_in_repo\":false,\"shipped\":true,\"sinks\":[],\"source_local\":true,\"source_near\":true}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "affected_relationship={\"evidence\":[\"base_span:api.js:2-2\",\"head_span:api.js:2-2\",\"unchanged_line:1;lexical_only\",\"unchanged_line:3;lexical_only\",\"unchanged_line:4;lexical_only\"],\"kind\":\"entry\",\"source\":{\"family\":\"output_encoding\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"},\"target\":{\"family\":\"output_encoding\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"}}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "path" + }, + "priority": -4.0, + "evidence": [ + "static_rank=1.0", + "normalized_evidence={\"access_declared_public\":true,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":true,\"family_in_repo\":false,\"shipped\":true,\"sinks\":[],\"source_local\":true,\"source_near\":true}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "affected_relationship={\"evidence\":[\"base_span:api.js:2-2\",\"head_span:api.js:2-2\",\"unchanged_line:1;lexical_only\",\"unchanged_line:3;lexical_only\",\"unchanged_line:4;lexical_only\"],\"kind\":\"entry\",\"source\":{\"family\":\"path\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"},\"target\":{\"family\":\"path\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"}}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "prototype" + }, + "priority": -5.0, + "evidence": [ + "static_rank=1.0", + "normalized_evidence={\"access_declared_public\":true,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":true,\"family_in_repo\":false,\"shipped\":true,\"sinks\":[],\"source_local\":true,\"source_near\":true}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "affected_relationship={\"evidence\":[\"base_span:api.js:2-2\",\"head_span:api.js:2-2\",\"unchanged_line:1;lexical_only\",\"unchanged_line:3;lexical_only\",\"unchanged_line:4;lexical_only\"],\"kind\":\"entry\",\"source\":{\"family\":\"prototype\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"},\"target\":{\"family\":\"prototype\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"}}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "untrusted_destination" + }, + "priority": -6.0, + "evidence": [ + "static_rank=1.0", + "normalized_evidence={\"access_declared_public\":true,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":true,\"family_in_repo\":false,\"shipped\":true,\"sinks\":[],\"source_local\":true,\"source_near\":true}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "affected_relationship={\"evidence\":[\"base_span:api.js:2-2\",\"head_span:api.js:2-2\",\"unchanged_line:1;lexical_only\",\"unchanged_line:3;lexical_only\",\"unchanged_line:4;lexical_only\"],\"kind\":\"entry\",\"source\":{\"family\":\"untrusted_destination\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"},\"target\":{\"family\":\"untrusted_destination\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"}}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "config_secrets" + }, + "priority": -7.0, + "evidence": [ + "static_rank=0.15", + "taint_evidence_not_applicable", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ], + "tier": null, + "score": null + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "injection" + }, + "priority": -8.0, + "evidence": [ + "static_rank=0.15", + "normalized_evidence={\"access_declared_public\":false,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":false,\"family_in_repo\":true,\"shipped\":true,\"sinks\":[],\"source_local\":false,\"source_near\":false}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "output_encoding" + }, + "priority": -9.0, + "evidence": [ + "static_rank=0.15", + "normalized_evidence={\"access_declared_public\":false,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":false,\"family_in_repo\":false,\"shipped\":true,\"sinks\":[],\"source_local\":false,\"source_near\":false}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "path" + }, + "priority": -10.0, + "evidence": [ + "static_rank=0.15", + "normalized_evidence={\"access_declared_public\":false,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":false,\"family_in_repo\":false,\"shipped\":true,\"sinks\":[],\"source_local\":false,\"source_near\":false}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "prototype" + }, + "priority": -11.0, + "evidence": [ + "static_rank=0.15", + "normalized_evidence={\"access_declared_public\":false,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":false,\"family_in_repo\":false,\"shipped\":true,\"sinks\":[],\"source_local\":false,\"source_near\":false}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "untrusted_destination" + }, + "priority": -12.0, + "evidence": [ + "static_rank=0.15", + "normalized_evidence={\"access_declared_public\":false,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":false,\"family_in_repo\":false,\"shipped\":true,\"sinks\":[],\"source_local\":false,\"source_near\":false}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + } + ], + "deferred": [], + "unresolved_boundaries": [ + "context_projection_unavailable:contributor-scan:7ad5ff7dd4882f08a395c2c0f55de6f6a3ddb9f9db1536b207572a65485776ac", + "context_projection_unavailable:contributor-scan:010672f395d61fff5ca6acb6e5fb3e3aae4dcf9c30887240b985e705b656717a", + "dynamic_external_or_depth_context_unresolved:contributor-scan:e3453476e53cbccfd684460af10a161bb890fc026f3d10e18b30405d62a3d6cd", + "dynamic_external_or_depth_context_unresolved:contributor-scan:7a8bdefd0721637c5f48e97e26439ce8b329b293d8f6e65367e9e0c362f27b1d", + "dynamic_external_or_depth_context_unresolved:contributor-scan:1f94e3ba635eccd3f41986a6d98fc72c3f80358fe857e753b320e35cf6b73d64", + "dynamic_external_or_depth_context_unresolved:contributor-scan:3e5e7ffa9d58f7410260699db0971f16db0f439393765944fe1fb80eadda97e8", + "context_projection_unavailable:contributor-scan:1354f0d585180f0836d0688fa02f6e277e23082a8586e943e6d14a6b6c674937", + "context_scope_empty:contributor-scan:a707b5a8dd0057e55f54f4a8920d895cebf04fa14b9a7464a40ac60b9ba5002f", + "context_scope_empty:contributor-scan:915e4a4f1f95ecd70094688f73feb49a8be41d7a7a151d8e35f2d42035cecdf0", + "context_scope_empty:contributor-scan:f73e64bc02e13221cf6c8e8ae697e1ba2857957157a9b2fd216e556bd8d57a1a", + "context_scope_empty:contributor-scan:66f58a5dc208fab2d2634273b2f4b4048e593b4933ccfb1093baf39c04027ab5", + "context_scope_empty:contributor-scan:1c1ec68dedd385fa5c98bd93ae642d8384a6fa384470cd8290623842d921bfa2" + ] + } + ], + "completed_questions": [ + { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "injection" + } + ], + "coverage_status": "incomplete" + } + ], + "finding_status": "none", + "coverage_status": "incomplete", + "push_disposition": "allow", + "actionable_defect_ids": [] + }, + "artifact": "/results/warm-identical.json" + }, + { + "name": "uncached", + "elapsed_seconds": 130.4318098879885, + "exit_code": 0, + "timings": { + "deadline_seconds": 600.0, + "cancellation_allowance_seconds": 2.0, + "resolution_seconds": 0.01867874909657985, + "provenance_seconds": 0.28871102491393685, + "snapshot_seconds": 0.018826198996976018, + "snapshot_reuses": 0, + "discovery_seconds": 0.11859792796894908, + "discovery_hits": 0, + "preparation_seconds": 0.15741463494487107, + "head_seconds": 64.25150758202653, + "base_seconds": 65.24028130201623, + "admission_seconds": 0.005215354030951858, + "cleanup_seconds": 0.00034814607352018356, + "graph_hits": 0, + "query_hits": 0, + "total_seconds": 129.98746631806716 + }, + "result": { + "analyses": [ + { + "comparison": { + "head_oid": "458817181ffff46da686825cdea0e27189fb447d", + "base_oid": "54b3130ca5354201a00bbd841640f6cb8651aaf8", + "base_reason": "explicit_local_replay", + "refs": [ + "refs/heads/replay" + ] + }, + "scopes": [ + { + "ranker_version": "existing-static-evidence-v1", + "ranking_mode": "evidence", + "population_complete": true, + "selected": [ + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "access_control" + }, + "priority": 0.0, + "evidence": [ + "static_rank=1.0", + "taint_evidence_not_applicable", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ], + "tier": null, + "score": null + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "config_secrets" + }, + "priority": -1.0, + "evidence": [ + "static_rank=1.0", + "taint_evidence_not_applicable", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ], + "tier": null, + "score": null + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "injection" + }, + "priority": -2.0, + "evidence": [ + "static_rank=1.0", + "normalized_evidence={\"access_declared_public\":true,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":true,\"family_in_repo\":true,\"shipped\":true,\"sinks\":[{\"arg0_literal\":false,\"arity\":1,\"cleansed_on_call\":false,\"code\":\"eval(value)\",\"line\":3,\"method\":\"handler\"}],\"source_local\":true,\"source_near\":true}", + "tier=4", + "score=5.5", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "affected_relationship={\"evidence\":[\"base_span:api.js:2-2\",\"head_span:api.js:2-2\",\"unchanged_line:1;lexical_only\",\"unchanged_line:3;lexical_only\",\"unchanged_line:4;lexical_only\"],\"kind\":\"entry\",\"source\":{\"family\":\"injection\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"},\"target\":{\"family\":\"injection\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"}}" + ], + "tier": 4, + "score": 5.5 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "output_encoding" + }, + "priority": -3.0, + "evidence": [ + "static_rank=1.0", + "normalized_evidence={\"access_declared_public\":true,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":true,\"family_in_repo\":false,\"shipped\":true,\"sinks\":[],\"source_local\":true,\"source_near\":true}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "affected_relationship={\"evidence\":[\"base_span:api.js:2-2\",\"head_span:api.js:2-2\",\"unchanged_line:1;lexical_only\",\"unchanged_line:3;lexical_only\",\"unchanged_line:4;lexical_only\"],\"kind\":\"entry\",\"source\":{\"family\":\"output_encoding\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"},\"target\":{\"family\":\"output_encoding\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"}}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "path" + }, + "priority": -4.0, + "evidence": [ + "static_rank=1.0", + "normalized_evidence={\"access_declared_public\":true,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":true,\"family_in_repo\":false,\"shipped\":true,\"sinks\":[],\"source_local\":true,\"source_near\":true}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "affected_relationship={\"evidence\":[\"base_span:api.js:2-2\",\"head_span:api.js:2-2\",\"unchanged_line:1;lexical_only\",\"unchanged_line:3;lexical_only\",\"unchanged_line:4;lexical_only\"],\"kind\":\"entry\",\"source\":{\"family\":\"path\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"},\"target\":{\"family\":\"path\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"}}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "prototype" + }, + "priority": -5.0, + "evidence": [ + "static_rank=1.0", + "normalized_evidence={\"access_declared_public\":true,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":true,\"family_in_repo\":false,\"shipped\":true,\"sinks\":[],\"source_local\":true,\"source_near\":true}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "affected_relationship={\"evidence\":[\"base_span:api.js:2-2\",\"head_span:api.js:2-2\",\"unchanged_line:1;lexical_only\",\"unchanged_line:3;lexical_only\",\"unchanged_line:4;lexical_only\"],\"kind\":\"entry\",\"source\":{\"family\":\"prototype\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"},\"target\":{\"family\":\"prototype\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"}}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "untrusted_destination" + }, + "priority": -6.0, + "evidence": [ + "static_rank=1.0", + "normalized_evidence={\"access_declared_public\":true,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":true,\"family_in_repo\":false,\"shipped\":true,\"sinks\":[],\"source_local\":true,\"source_near\":true}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "affected_relationship={\"evidence\":[\"base_span:api.js:2-2\",\"head_span:api.js:2-2\",\"unchanged_line:1;lexical_only\",\"unchanged_line:3;lexical_only\",\"unchanged_line:4;lexical_only\"],\"kind\":\"entry\",\"source\":{\"family\":\"untrusted_destination\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"},\"target\":{\"family\":\"untrusted_destination\",\"function\":\"handler\",\"language\":\"javascript\",\"path\":\"api.js\",\"unit\":\"repository\"}}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "config_secrets" + }, + "priority": -7.0, + "evidence": [ + "static_rank=0.15", + "taint_evidence_not_applicable", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ], + "tier": null, + "score": null + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "injection" + }, + "priority": -8.0, + "evidence": [ + "static_rank=0.15", + "normalized_evidence={\"access_declared_public\":false,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":false,\"family_in_repo\":true,\"shipped\":true,\"sinks\":[],\"source_local\":false,\"source_near\":false}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "output_encoding" + }, + "priority": -9.0, + "evidence": [ + "static_rank=0.15", + "normalized_evidence={\"access_declared_public\":false,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":false,\"family_in_repo\":false,\"shipped\":true,\"sinks\":[],\"source_local\":false,\"source_near\":false}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "path" + }, + "priority": -10.0, + "evidence": [ + "static_rank=0.15", + "normalized_evidence={\"access_declared_public\":false,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":false,\"family_in_repo\":false,\"shipped\":true,\"sinks\":[],\"source_local\":false,\"source_near\":false}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "prototype" + }, + "priority": -11.0, + "evidence": [ + "static_rank=0.15", + "normalized_evidence={\"access_declared_public\":false,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":false,\"family_in_repo\":false,\"shipped\":true,\"sinks\":[],\"source_local\":false,\"source_near\":false}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "untrusted_destination" + }, + "priority": -12.0, + "evidence": [ + "static_rank=0.15", + "normalized_evidence={\"access_declared_public\":false,\"bound_names\":[\"execute\",\"query\"],\"carried\":false,\"carried_weak\":false,\"entry\":false,\"family_in_repo\":false,\"shipped\":true,\"sinks\":[],\"source_local\":false,\"source_near\":false}", + "tier=0", + "score=0.0", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"458817181ffff46da686825cdea0e27189fb447d\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ], + "tier": 0, + "score": 0.0 + } + ], + "deferred": [], + "unresolved_boundaries": [ + "context_projection_unavailable:contributor-scan:7ad5ff7dd4882f08a395c2c0f55de6f6a3ddb9f9db1536b207572a65485776ac", + "context_projection_unavailable:contributor-scan:010672f395d61fff5ca6acb6e5fb3e3aae4dcf9c30887240b985e705b656717a", + "dynamic_external_or_depth_context_unresolved:contributor-scan:e3453476e53cbccfd684460af10a161bb890fc026f3d10e18b30405d62a3d6cd", + "dynamic_external_or_depth_context_unresolved:contributor-scan:7a8bdefd0721637c5f48e97e26439ce8b329b293d8f6e65367e9e0c362f27b1d", + "dynamic_external_or_depth_context_unresolved:contributor-scan:1f94e3ba635eccd3f41986a6d98fc72c3f80358fe857e753b320e35cf6b73d64", + "dynamic_external_or_depth_context_unresolved:contributor-scan:3e5e7ffa9d58f7410260699db0971f16db0f439393765944fe1fb80eadda97e8", + "context_projection_unavailable:contributor-scan:1354f0d585180f0836d0688fa02f6e277e23082a8586e943e6d14a6b6c674937", + "context_scope_empty:contributor-scan:a707b5a8dd0057e55f54f4a8920d895cebf04fa14b9a7464a40ac60b9ba5002f", + "context_scope_empty:contributor-scan:915e4a4f1f95ecd70094688f73feb49a8be41d7a7a151d8e35f2d42035cecdf0", + "context_scope_empty:contributor-scan:f73e64bc02e13221cf6c8e8ae697e1ba2857957157a9b2fd216e556bd8d57a1a", + "context_scope_empty:contributor-scan:66f58a5dc208fab2d2634273b2f4b4048e593b4933ccfb1093baf39c04027ab5", + "context_scope_empty:contributor-scan:1c1ec68dedd385fa5c98bd93ae642d8384a6fa384470cd8290623842d921bfa2" + ] + } + ], + "completed_questions": [ + { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "injection" + } + ], + "coverage_status": "incomplete" + } + ], + "finding_status": "none", + "coverage_status": "incomplete", + "push_disposition": "allow", + "actionable_defect_ids": [] + }, + "artifact": "/results/uncached.json" + }, + { + "name": "dependency-edit", + "elapsed_seconds": 30.458495798055083, + "exit_code": 0, + "timings": { + "deadline_seconds": 30.0, + "cancellation_allowance_seconds": 2.0, + "resolution_seconds": 0.015588726033456624, + "provenance_seconds": 0.2984913958935067, + "snapshot_seconds": 0.021403303020633757, + "snapshot_reuses": 0, + "discovery_seconds": 0.10751347593031824, + "discovery_hits": 1, + "preparation_seconds": 0.14761024806648493, + "head_seconds": 29.52236297901254, + "base_seconds": 5.339505150914192e-05, + "admission_seconds": 0.0002843959955498576, + "graph_hits": 0, + "query_hits": 0, + "total_seconds": 30.008655319106765 + }, + "result": { + "analyses": [ + { + "comparison": { + "head_oid": "1dfa34ea3bbf2d7850d4dd7ab533a911885b2ae8", + "base_oid": "54b3130ca5354201a00bbd841640f6cb8651aaf8", + "base_reason": "explicit_local_replay", + "refs": [ + "refs/heads/replay" + ] + }, + "scopes": [ + { + "ranker_version": "existing-static-evidence-v1", + "ranking_mode": "evidence", + "population_complete": true, + "selected": [], + "deferred": [ + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "access_control" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=1.0", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\",\"7061636b6167652e6a736f6e\"],\"declaration_paths\":[\"7061636b6167652e6a736f6e\"],\"deleted_paths\":[],\"head_revision\":\"1dfa34ea3bbf2d7850d4dd7ab533a911885b2ae8\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "config_secrets" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=1.0", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\",\"7061636b6167652e6a736f6e\"],\"declaration_paths\":[\"7061636b6167652e6a736f6e\"],\"deleted_paths\":[],\"head_revision\":\"1dfa34ea3bbf2d7850d4dd7ab533a911885b2ae8\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "injection" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=1.0", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\",\"7061636b6167652e6a736f6e\"],\"declaration_paths\":[\"7061636b6167652e6a736f6e\"],\"deleted_paths\":[],\"head_revision\":\"1dfa34ea3bbf2d7850d4dd7ab533a911885b2ae8\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "output_encoding" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=1.0", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\",\"7061636b6167652e6a736f6e\"],\"declaration_paths\":[\"7061636b6167652e6a736f6e\"],\"deleted_paths\":[],\"head_revision\":\"1dfa34ea3bbf2d7850d4dd7ab533a911885b2ae8\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "path" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=1.0", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\",\"7061636b6167652e6a736f6e\"],\"declaration_paths\":[\"7061636b6167652e6a736f6e\"],\"deleted_paths\":[],\"head_revision\":\"1dfa34ea3bbf2d7850d4dd7ab533a911885b2ae8\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "prototype" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=1.0", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\",\"7061636b6167652e6a736f6e\"],\"declaration_paths\":[\"7061636b6167652e6a736f6e\"],\"deleted_paths\":[],\"head_revision\":\"1dfa34ea3bbf2d7850d4dd7ab533a911885b2ae8\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "untrusted_destination" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=1.0", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\",\"7061636b6167652e6a736f6e\"],\"declaration_paths\":[\"7061636b6167652e6a736f6e\"],\"deleted_paths\":[],\"head_revision\":\"1dfa34ea3bbf2d7850d4dd7ab533a911885b2ae8\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "config_secrets" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=0.15", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\",\"7061636b6167652e6a736f6e\"],\"declaration_paths\":[\"7061636b6167652e6a736f6e\"],\"deleted_paths\":[],\"head_revision\":\"1dfa34ea3bbf2d7850d4dd7ab533a911885b2ae8\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "injection" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=0.15", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\",\"7061636b6167652e6a736f6e\"],\"declaration_paths\":[\"7061636b6167652e6a736f6e\"],\"deleted_paths\":[],\"head_revision\":\"1dfa34ea3bbf2d7850d4dd7ab533a911885b2ae8\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "output_encoding" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=0.15", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\",\"7061636b6167652e6a736f6e\"],\"declaration_paths\":[\"7061636b6167652e6a736f6e\"],\"deleted_paths\":[],\"head_revision\":\"1dfa34ea3bbf2d7850d4dd7ab533a911885b2ae8\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "path" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=0.15", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\",\"7061636b6167652e6a736f6e\"],\"declaration_paths\":[\"7061636b6167652e6a736f6e\"],\"deleted_paths\":[],\"head_revision\":\"1dfa34ea3bbf2d7850d4dd7ab533a911885b2ae8\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "prototype" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=0.15", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\",\"7061636b6167652e6a736f6e\"],\"declaration_paths\":[\"7061636b6167652e6a736f6e\"],\"deleted_paths\":[],\"head_revision\":\"1dfa34ea3bbf2d7850d4dd7ab533a911885b2ae8\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "untrusted_destination" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=0.15", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\",\"7061636b6167652e6a736f6e\"],\"declaration_paths\":[\"7061636b6167652e6a736f6e\"],\"deleted_paths\":[],\"head_revision\":\"1dfa34ea3bbf2d7850d4dd7ab533a911885b2ae8\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + } + ], + "unresolved_boundaries": [ + "configuration_dependency_projection_unavailable:contributor-scan", + "change_context_deadline_exhausted", + "deadline_exhausted" + ] + } + ], + "completed_questions": [], + "coverage_status": "incomplete" + } + ], + "finding_status": "none", + "coverage_status": "incomplete", + "push_disposition": "allow", + "actionable_defect_ids": [] + }, + "artifact": "/results/dependency-edit.json" + }, + { + "name": "one-function-edit", + "elapsed_seconds": 30.47911988804117, + "exit_code": 0, + "timings": { + "deadline_seconds": 30.0, + "cancellation_allowance_seconds": 2.0, + "resolution_seconds": 0.0180521160364151, + "provenance_seconds": 0.3072873749770224, + "snapshot_seconds": 0.01567274401895702, + "snapshot_reuses": 0, + "discovery_seconds": 0.09289419103879482, + "discovery_hits": 1, + "preparation_seconds": 0.12448294495698065, + "head_seconds": 29.538544582901523, + "base_seconds": 8.924305438995361e-05, + "admission_seconds": 0.0003181110369041562, + "graph_hits": 0, + "query_hits": 0, + "total_seconds": 30.013175226049498 + }, + "result": { + "analyses": [ + { + "comparison": { + "head_oid": "55267520b163df2b09174a99caa3932e716cd68e", + "base_oid": "54b3130ca5354201a00bbd841640f6cb8651aaf8", + "base_reason": "explicit_local_replay", + "refs": [ + "refs/heads/replay" + ] + }, + "scopes": [ + { + "ranker_version": "existing-static-evidence-v1", + "ranking_mode": "evidence", + "population_complete": true, + "selected": [], + "deferred": [ + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "access_control" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=1.0", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"55267520b163df2b09174a99caa3932e716cd68e\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "config_secrets" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=1.0", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"55267520b163df2b09174a99caa3932e716cd68e\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "injection" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=1.0", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"55267520b163df2b09174a99caa3932e716cd68e\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "output_encoding" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=1.0", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"55267520b163df2b09174a99caa3932e716cd68e\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "path" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=1.0", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"55267520b163df2b09174a99caa3932e716cd68e\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "prototype" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=1.0", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"55267520b163df2b09174a99caa3932e716cd68e\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "handler", + "family": "untrusted_destination" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=1.0", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"55267520b163df2b09174a99caa3932e716cd68e\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "config_secrets" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=0.15", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"55267520b163df2b09174a99caa3932e716cd68e\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "injection" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=0.15", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"55267520b163df2b09174a99caa3932e716cd68e\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "output_encoding" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=0.15", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"55267520b163df2b09174a99caa3932e716cd68e\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "path" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=0.15", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"55267520b163df2b09174a99caa3932e716cd68e\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "prototype" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=0.15", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"55267520b163df2b09174a99caa3932e716cd68e\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + }, + { + "identity": { + "unit": "repository", + "language": "javascript", + "path": "api.js", + "function": "", + "family": "untrusted_destination" + }, + "reason": "deadline_exhausted", + "evidence": [ + "static_rank=0.15", + "analysis_not_started=deadline_exhausted", + "change_sites={\"base_revision\":\"54b3130ca5354201a00bbd841640f6cb8651aaf8\",\"changed_paths\":[\"6170692e6a73\"],\"declaration_paths\":[],\"deleted_paths\":[],\"head_revision\":\"55267520b163df2b09174a99caa3932e716cd68e\",\"path_encoding\":\"filesystem-bytes-hex\"}", + "change_context_incomplete" + ] + } + ], + "unresolved_boundaries": [ + "change_context_deadline_exhausted", + "deadline_exhausted" + ] + } + ], + "completed_questions": [], + "coverage_status": "incomplete" + } + ], + "finding_status": "none", + "coverage_status": "incomplete", + "push_disposition": "allow", + "actionable_defect_ids": [] + }, + "artifact": "/results/one-function-edit.json" + } + ], + "source_bytes": 109, + "rollout": "not qualified; authored development control only" + }, + "same_revision_uncached_hook": { + "command": [ + "python", + "-m", + "openultrasast.cli", + "pre-push", + "/results/repo", + "--base", + "54b3130ca5354201a00bbd841640f6cb8651aaf8", + "--head", + "458817181ffff46da686825cdea0e27189fb447d", + "--artifact", + "/results/uncached-hook.json", + "--deadline", + "30", + "--max-regions", + "20" + ], + "exit_code": 0, + "elapsed_seconds": 30.606204619980417 + }, + "completed_question_controls": { + "warm_identical_30_seconds": 5, + "uncached_same_revisions_30_seconds": 0, + "head_supported_injection": 1, + "base_comparison_checks": 4 + }, + "normal_alerts": 0, + "registry": "empty; new diagnostic injection denied admission for capability_unavailable", + "provenance": { + "engine": "joern-4.0.625", + "facts": "32edb6e1a78f1afb08ac4248cd436be16663e0d90dc1b3aaf948f2ce49a5a8f0", + "queries": "7bf090e053331f03d04c9290c8a593d00efd4d390f4b77b8382c2befc47996cc", + "policy": "b0ad9ee436fb4a71e3ff3d40f7be604c780562859cfcee491f4d201ec45cba04", + "repository": "/results/repo", + "model": "disabled", + "requested_base": "54b3130ca5354201a00bbd841640f6cb8651aaf8", + "requested_head": "458817181ffff46da686825cdea0e27189fb447d", + "engine_runtime": "2b415139532d8d3a0c6e46ecdac26cd3d792efc2bae441f0b7e39bb28ec5d16e", + "graph_layout": "060428173f47cf33bdf5e532d4069feb598ba0e8c7ea30a60888c8e4cfb37a42", + "core": "36ac9a045b8b8ad943eea1f511924caae54750a9d5d45da776ebefc460f08eab", + "semantics": "4558faecc3f33aaab98a65672550c3acb4e142689a827c696fd29a9bdd1bf78c", + "ranking_mode": "evidence", + "capabilities": "empty_registry_pending_task_8.3", + "cache": "validated_local_reuse" + }, + "validation": { + "pytest": "1268 passed, 9 skipped in 108.74s", + "ruff": "PASS", + "format": "PASS (250 files)", + "mypy": "PASS (114 source files)", + "compileall": "PASS", + "detection_gate": "PASS 44/47, FP 0/44", + "map_gate": "PASS", + "pair_gate": "PASS local 3/3; labeled 28/28; fix leaks 0", + "docker_build": "PASS", + "packaged_cli_smoke": "PASS", + "manual_review_fallback": true + }, + "bundles": [ + { + "path": "2026-09-13-reuse-final-control.json.gz", + "sha256": "c8a4306450166a74f590e8f23018df5a6c333d05f8f3a5924b729c4e5206a111", + "format": "gzip JSON object mapping original filenames to exact UTF-8 content" + }, + { + "path": "2026-09-13-reuse-initial-control.json.gz", + "sha256": "20fd916aed04c9ca829e1ecbc149720335587e7245ec2289e8c5c9be8743995c", + "format": "gzip JSON object mapping original filenames to exact UTF-8 content" + } + ], + "decision": "Group 6 reuse correctness PASS; rollout NO-GO. Source/dependency edits exhaust the 30-second deadline, representative p95/completion and independent precision/recall populations remain unqualified." +} diff --git a/benchmarks/measurements/2026-09-13-reuse-final-control.json.gz b/benchmarks/measurements/2026-09-13-reuse-final-control.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..c15347e59ad4f13d56a7e26387331a8e4d6c01b4 GIT binary patch literal 35497 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 YS!viB!$=QBRv&);KYHTU(mXo}03%*>-v9sr literal 0 HcmV?d00001 diff --git a/benchmarks/measurements/2026-09-13-reuse-initial-control.json.gz b/benchmarks/measurements/2026-09-13-reuse-initial-control.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..fe608e82b6890e9b74a357414aee0403014629d8 GIT binary patch literal 26835 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 0 +git("add", "api.js") +git("commit", "-m", "fixed") +base = git("rev-parse", "HEAD") +source.write_text(source.read_text().replace('"fixed"', "req.body.value")) +git("add", "api.js") +git("commit", "-m", "introduce") +head = git("rev-parse", "HEAD") +runs = [] + + +def run(name, deadline, cache=True, tip=head): + artifact = out / (name + ".json") + cmd = [ + "python", + "-m", + "openultrasast.cli", + "pre-push", + str(repo), + "--base", + base, + "--head", + tip, + "--artifact", + str(artifact), + "--deadline", + str(deadline), + "--max-regions", + "20", + ] + if cache: + cmd += ["--cache-dir", str(out / "cache")] + start = time.monotonic() + process = subprocess.run(cmd, capture_output=True, text=True, timeout=deadline + 10) + elapsed = time.monotonic() - start + (out / (name + ".stdout")).write_text(process.stdout) + (out / (name + ".stderr")).write_text(process.stderr) + assert artifact.is_file(), (process.returncode, process.stdout, process.stderr) + data = json.loads(artifact.read_text()) + run = { + "name": name, + "elapsed_seconds": elapsed, + "exit_code": process.returncode, + "timings": data["timings"], + "result": data["result"], + "artifact": str(artifact), + } + runs.append(run) + (out / "runs.json").write_text(json.dumps(runs, indent=2)) + print(json.dumps(run), flush=True) + return data + + +populated = run("populate", 600) +warm = run("warm-identical", 30) +cold = run("uncached", 600, False) +(repo / "package.json").write_text('{"dependencies":{"express":"5.0.0"}}\n') +git("add", "package.json") +git("commit", "-m", "dependency change") +changed = git("rev-parse", "HEAD") +run("dependency-edit", 30, tip=changed) +git("rm", "package.json") +source.write_text(source.read_text().replace("req.body.value", 'req.body.value + "!"')) +git("add", "api.js") +git("commit", "-m", "one function edit") +edited = git("rev-parse", "HEAD") +run("one-function-edit", 30, tip=edited) + + +def evidence(data): + return [ + { + "side": s["side"], + "scope": s["scan"]["scope"], + "questions": s["scan"]["question_outcomes"], + "findings": s["scan"]["findings"], + "degradations": s["scan"]["degradations"], + } + for s in data["scans"] + ] + + +summary = { + "cold_populated_evidence_equal": evidence(cold) == evidence(populated), + "cold_warm_evidence_equal": evidence(cold) == evidence(warm), + "admission_equal": cold["admission"] == warm["admission"], + "runs": runs, + "source_bytes": len(source.read_bytes()), + "rollout": "not qualified; authored development control only", +} +(out / "summary.json").write_text(json.dumps(summary, indent=2)) +print(json.dumps(summary), flush=True) +assert summary["cold_populated_evidence_equal"] and summary["cold_warm_evidence_equal"] and summary["admission_equal"] +assert warm["timings"]["graph_hits"] == 2 and warm["timings"]["query_hits"] > 0 +assert any(item["candidate"]["delta"]["novelty"] == "new" for item in warm["admission"]["dispositions"]) +assert not any(data["admission"]["defects"] for data in (populated, warm, cold)) +assert runs[1]["elapsed_seconds"] <= 32 +assert all( + r["timings"]["graph_hits"] <= 1 and r["elapsed_seconds"] <= 32 for r in runs if r["name"] in ("dependency-edit", "one-function-edit") +) diff --git a/ops/README.md b/ops/README.md index aa13e28..c242f29 100644 --- a/ops/README.md +++ b/ops/README.md @@ -266,3 +266,19 @@ it does not demand a positive after an interrupted query. Run with the network-d image, mounting the smoke script read-only as for the other smoke scripts. Snapshot manifests record verified immutable source bytes; full scan records retain exact selected/deferred questions, raw answers, per-question gaps, change context and candidate dispositions. + +Experimental replay can reuse compatible local preparation, graphs and query evidence: + +```sh +ousast pre-push /path/to/repo --base BASE --head HEAD \ + --artifact /path/outside/repo/result.json \ + --cache-dir /path/outside/repo/private-cache +``` + +Omit `--cache-dir` for a cold control. The cache directory must be owned by the current +user, private (0700), and outside the analyzed repository. Its default payload/manifest +budget is 2 GiB (`PushConfig.cache_max_bytes` for API callers). Cache permission to reuse +is separate from alert eligibility: current admission is always applied. No hook is installed +by replay. Artifact timings record discovery, graph and query hits; identical-tip hits do +not establish representative changed-code latency. Corrupt, partial or incompatible entries +are misses, with remaining work constrained by the same push deadline. diff --git a/src/openultrasast/cli.py b/src/openultrasast/cli.py index ea25539..de143fc 100644 --- a/src/openultrasast/cli.py +++ b/src/openultrasast/cli.py @@ -115,6 +115,9 @@ def main(argv: list[str] | None = None) -> int: replay_parser.add_argument("--base", required=True) replay_parser.add_argument("--head", required=True) replay_parser.add_argument("--artifact", type=Path, required=True) + replay_parser.add_argument( + "--cache-dir", type=Path, help="reuse compatible local artifacts in a private directory outside the repository" + ) replay_parser.add_argument("--deadline", type=float, default=30.0) replay_parser.add_argument("--cancellation-allowance", type=float, default=2.0) replay_parser.add_argument("--max-regions", type=int, default=500) @@ -212,7 +215,13 @@ def main(argv: list[str] | None = None) -> int: incomplete_coverage_policy=args.incomplete_coverage, ) delivery = replay( - args.path, base=args.base, head=args.head, artifact=args.artifact, config=settings, max_regions=args.max_regions + args.path, + base=args.base, + head=args.head, + artifact=args.artifact, + config=settings, + max_regions=args.max_regions, + cache_dir=args.cache_dir, ) except ValueError as error: parser.error(str(error)) diff --git a/src/openultrasast/cpg/backend.py b/src/openultrasast/cpg/backend.py index c62e19e..7d78881 100644 --- a/src/openultrasast/cpg/backend.py +++ b/src/openultrasast/cpg/backend.py @@ -266,6 +266,41 @@ class NullBackend: return None +def engine_runtime_identity( + *, include_tests: bool = True, language: str | None = None, execution_budget: ExecutionBudget | None = None +) -> str: + """Installed frontend/adaptation bytes, also used by comparison eligibility provenance.""" + from .artifact import digest_value, graph_bytes + + budget = execution_budget + deadline = budget.deadline_monotonic if budget else float("inf") + launcher = shutil.which("joern") + version = joern_version() + installation = [] + if launcher is not None and version is not None: + home = Path(launcher).resolve().parent + engine = ".".join(map(str, version)) + frontends = {_FRONTENDS.get(language, "joern-parse")} if language else set(_FRONTENDS.values()) + candidates = {home / f"lib/io.joern.joern-cli-{engine}.jar", Path(launcher).resolve()} + for frontend in frontends: + candidates.add(home / f"frontends/{frontend}/lib/io.joern.{frontend}-{engine}.jar") + frontend_launcher = shutil.which(frontend) + if frontend_launcher is not None: + candidates.add(Path(frontend_launcher).resolve()) + if "jssrc2cpg" in frontends: + candidates.update({home / "ousast-frontend-retention-v1/manifest.json", home / "frontends/jssrc2cpg/bin/astgen/astgen-linux"}) + for candidate in sorted(candidates): + installation.append((candidate.name, graph_bytes(candidate, deadline) if candidate.is_file() else "absent")) + return digest_value( + { + "version": version, + "files": installation, + "include_tests": include_tests, + "java_options": os.environ.get("JAVA_TOOL_OPTIONS", ""), + } + ) + + @dataclass class JoernBackend: """``joern-parse`` then ``joern --script``, both out of process and both timeout-bounded.""" @@ -324,7 +359,8 @@ class JoernBackend: ) -> GraphIdentity: from .artifact import digest_value, read_bytes, source_inventory - deadline = execution_budget.deadline_monotonic if execution_budget else float("inf") + budget = execution_budget or self.execution_budget + deadline = budget.deadline_monotonic if budget else float("inf") version = joern_version() if version is None: raise ValueError("engine_identity_unknown") @@ -342,7 +378,16 @@ class JoernBackend: engine, (DATAFLOW_OVERLAY,), (("implementation", code),), - (("include_tests", str(self.include_tests)), ("java_options", os.environ.get("JAVA_TOOL_OPTIONS", "default") or "default")), + ( + ("include_tests", str(self.include_tests)), + ( + "installation", + engine_runtime_identity( + include_tests=self.include_tests, language=language, execution_budget=execution_budget or self.execution_budget + ), + ), + ("java_options", os.environ.get("JAVA_TOOL_OPTIONS", "default") or "default"), + ), ) def describe_graph( diff --git a/src/openultrasast/push/reuse.py b/src/openultrasast/push/reuse.py new file mode 100644 index 0000000..dccdac2 --- /dev/null +++ b/src/openultrasast/push/reuse.py @@ -0,0 +1,202 @@ +"""Compatible replay reuse; the existing ranker and detector remain decision owners.""" + +from __future__ import annotations + +import math +import os +import time +from collections.abc import Callable, Mapping, Sequence +from dataclasses import asdict, replace +from pathlib import Path +from typing import Any + +from openultrasast.cpg.artifact import GraphArtifact, digest_value +from openultrasast.cpg.backend import CpgResult, JoernBackend +from openultrasast.model.contracts import ExecutionBudget +from openultrasast.model.regions import ScanRegion +from openultrasast.push.cache import ArtifactCache, SemanticKeys +from openultrasast.push.contracts import SnapshotManifest + + +class ReusingBackend: + def __init__(self, backend: JoernBackend, cache: ArtifactCache, semantics: SemanticKeys, *, declarations: str, exclusions: str) -> None: + self.backend = backend + self.cache = cache + self.semantics = semantics + self.declarations = declarations + self.exclusions = exclusions + self.last_failure = "" + self.graph_hits = 0 + self.query_hits = 0 + + def build(self, root: Path, *, language: str, exclude: Sequence[str], execution_budget: ExecutionBudget) -> CpgResult | None: + identity = None + artifact = None + result = None + try: + identity = self.backend.graph_identity( + root, + language=language, + declarations_digest=self.declarations, + exclusions_digest=digest_value([self.exclusions, sorted(exclude)]), + execution_budget=execution_budget, + ) + key = digest_value({"layer": "graph-v1", "identity": identity.to_payload()}) + with self.cache.lookup(key, execution_budget) as entry: + if entry is not None: + artifact = GraphArtifact.from_payload(entry.metadata) + result = self.backend.load_graph(entry.path, artifact, identity, execution_budget=execution_budget) + if result is not None: + self.graph_hits += 1 + except (OSError, ValueError, TimeoutError): + artifact = None + if result is None: + artifact = None + result = self.backend.build(root, language=language, exclude=exclude, execution_budget=execution_budget) + self.last_failure = self.backend.last_failure + if result is None: + return None + if identity is not None and time.monotonic() < execution_budget.deadline_monotonic: + artifact = self.backend.describe_graph(result, root, identity, execution_budget=execution_budget) + if artifact is not None: + self.cache.publish(key, result.cpg_path, artifact.to_payload(), execution_budget) + if artifact is None: + return result + return self._answers(result, artifact, execution_budget) + + def _answers(self, result: CpgResult, artifact: GraphArtifact, budget: ExecutionBudget) -> CpgResult: + def normalize(value: Any) -> Any: + if isinstance(value, str): + return value.replace(artifact.source_root + "/", "") + if isinstance(value, list): + return [normalize(item) for item in value] + if isinstance(value, dict): + return {key: normalize(item) for key, item in value.items()} + return value + + def batch(kind: str, requests: Mapping[str, Mapping[str, object]]) -> dict[str, list[object]] | None: + answers: dict[str, list[object]] = {} + missing = {} + keys = {} + for rid, request in requests.items(): + if time.monotonic() >= budget.deadline_monotonic: + break + key = self.semantics.query( + graph=digest_value({"identity": artifact.identity.to_payload(), "bytes": artifact.graph_digest}), + kind=kind, + request=request, + context={"paths": artifact.source_paths, "declarations": artifact.identity.declarations_digest}, + ) + keys[rid] = key + cached = self.cache.get_json(key, kind="query", budget=budget) + if isinstance(cached, dict) and isinstance(cached.get("rows"), list) and self._census(cached.get("census"), artifact): + answers[rid] = cached["rows"] + answers["__census__"] = cached["census"] + self.query_hits += 1 + else: + missing[rid] = request + if missing and time.monotonic() < budget.deadline_monotonic: + raw: Any = result.run_batch(kind, missing) if result.run_batch else None + if raw is None and result.run_batch is None: + raw = {rid: result.run(kind, params) for rid, params in missing.items()} + if isinstance(raw, dict): + normalized = normalize(raw) + census = normalized.get("__census__") + valid = self._census(census, artifact) + if not valid: + return answers or None + answers["__census__"] = census + for rid in missing: + rows = normalized.get(rid) + if isinstance(rows, list): + answers[rid] = rows + self.cache.put_json(keys[rid], {"rows": rows, "census": census}, kind="query", budget=budget, complete=valid) + return answers or None + + def run(kind: str, params: Mapping[str, object]) -> object | None: + if kind == "census": + value: object = normalize(result.run(kind, params)) + return value + rows = batch(kind, {"one": params}) + return rows.get("one") if rows is not None else None + + return replace(result, run=run, run_batch=batch) + + @staticmethod + def _census(value: object, artifact: GraphArtifact) -> bool: + if not isinstance(value, list) or len(value) != 1 or not isinstance(value[0], dict): + return False + row = value[0] + names = row.get("file_names") + try: + return ( + isinstance(names, list) + and all(isinstance(name, str) for name in names) + and set(artifact.source_paths) <= set(names) + and int(str(row.get("methods", 0))) > 0 + and int(str(row.get("files", 0))) >= len(artifact.source_paths) + and int(str(row.get("shards", 1))) == 1 + ) + except (ValueError, TypeError): + return False + + +def discovery( + root: Path, + manifest: SnapshotManifest, + *, + cache: ArtifactCache | None, + identity: str, + budget: ExecutionBudget, + discover: Callable[[Path], tuple[tuple[ScanRegion, ...], tuple[bytes, ...]]], +) -> tuple[tuple[ScanRegion, ...], tuple[bytes, ...], bool]: + """Persist pure discovery only after the adapter proves the complete snapshot readable.""" + key = digest_value( + { + "layer": "discovery-v1", + "files": [item.to_payload() for item in manifest.files], + "object_format": manifest.object_format, + "identity": identity, + } + ) + raw = cache.get_json(key, kind="discovery", budget=budget) if cache and manifest.complete else None + if isinstance(raw, dict): + try: + regions = [] + for item in raw["regions"]: + values = dict(item) + values["families"] = tuple(values["families"]) + region = ScanRegion(**values) + if ( + not all(isinstance(v, str) for v in (region.path, region.language, region.source, *region.families)) + or region.function is not None + and not isinstance(region.function, str) + or type(region.shipped) is not bool + or type(region.rank) not in (int, float) + or not math.isfinite(region.rank) + ): + raise ValueError("invalid_discovery_region") + if os.fsencode(region.path) not in {item.path_bytes for item in manifest.files}: + raise ValueError("discovery_path_outside_snapshot") + regions.append(region) + declarations = tuple(bytes.fromhex(item) for item in raw["declarations"]) + if not set(declarations) <= {item.path_bytes for item in manifest.files}: + raise ValueError("invalid_declarations") + return tuple(regions), declarations, True + except (KeyError, ValueError, TypeError): + pass + found, declarations = discover(root) + if cache and manifest.complete: + cache.put_json( + key, + {"regions": [asdict(region) for region in found], "declarations": [item.hex() for item in declarations]}, + kind="discovery", + budget=budget, + complete=True, + ) + return found, declarations, False + + +def declaration_identity(manifest: SnapshotManifest, paths: tuple[bytes, ...]) -> str: + declared = set(paths) + return digest_value([item.to_payload() for item in manifest.files if item.path_bytes in declared]) diff --git a/src/openultrasast/push/runner.py b/src/openultrasast/push/runner.py index 4f94b61..67e1987 100644 --- a/src/openultrasast/push/runner.py +++ b/src/openultrasast/push/runner.py @@ -9,18 +9,22 @@ import pickle import selectors import time from collections.abc import Callable +from contextlib import nullcontext from dataclasses import asdict, replace from pathlib import Path from typing import Any, Literal, TypeVar, cast from openultrasast.config import PushConfig -from openultrasast.cpg.backend import JoernBackend, joern_version +from openultrasast.cpg.artifact import digest_value +from openultrasast.cpg.backend import JoernBackend, engine_runtime_identity, joern_version from openultrasast.mapping import analyze_entry_points from openultrasast.model.contracts import ExecutionBudget +from openultrasast.model.layout import layout_facts from openultrasast.model.regions import ScanRegion, regions_for from openultrasast.model.scan import ScanBudget, scan_repository from openultrasast.model.shipped import declared_sources from openultrasast.preprocess import build_file_target, enumerate_source_files +from openultrasast.push.cache import ArtifactCache, SemanticKeys from openultrasast.push.contracts import ComparisonAnalysis, SnapshotManifest from openultrasast.push.policy import ( AdmissionCandidate, @@ -32,6 +36,7 @@ from openultrasast.push.policy import ( semantics_digest, ) from openultrasast.push.report import PushReport, ReportDelivery, ScanRecord, _completed_scan, deliver_report, render_report +from openultrasast.push.reuse import ReusingBackend, declaration_identity, discovery from openultrasast.push.snapshot import SnapshotAdapter _T = TypeVar("_T") @@ -124,14 +129,25 @@ def _provenance(repository: Path, scan_budget: ScanBudget) -> dict[str, str]: version = joern_version() engine = "joern-" + (".".join(map(str, version)) if version else "unavailable_or_unidentified") + runtime = engine_runtime_identity() + integration = digest(list((root / "cpg").glob("*.py"))) return { "repository": str(repository.absolute()), + "engine_runtime": runtime, "engine": engine, "facts": digest(list((root / "ruleset").rglob("*.toml"))), + "graph_layout": digest_value( + { + "facts": [asdict(row) for row in layout_facts()], + "implementation": digest([root / "model/layout.py", root / "model/partitions.py"]), + } + ), "queries": digest(list((root / "cpg/queries").glob("*.sc"))), "policy": digest(list((root / "push").glob("*.py"))), "core": digest(list((root / "model").glob("*.py")) + list((root / "cpg").glob("*.py"))), - "semantics": semantics_digest(engine_identity=engine, options=asdict(scan_budget)), + "semantics": semantics_digest( + engine_identity=engine, options={**asdict(scan_budget), "engine_runtime": runtime, "engine_integration": integration} + ), "ranking_mode": "evidence", "model": "disabled", "capabilities": "empty_registry_pending_task_8.3", @@ -139,6 +155,13 @@ def _provenance(repository: Path, scan_budget: ScanBudget) -> dict[str, str]: } +def _discovery_identity() -> str: + root = Path(__file__).resolve().parents[1] + # Discovery invokes the shipped mapper, preprocessing, layout and family specs. + # Hash code bytes, never a mutable import name or a repository-provided version. + return digest_value([(p.relative_to(root).as_posix(), hashlib.sha256(p.read_bytes()).hexdigest()) for p in sorted(root.rglob("*.py"))]) + + def replay( repository: Path, *, @@ -148,16 +171,22 @@ def replay( config: PushConfig | None = None, backend: Any = None, max_regions: int = 500, + cache_dir: Path | None = None, ) -> ReportDelivery: """Run a single explicit comparison; exit policy never stands for coverage.""" config = config or PushConfig() if max_regions < 1: raise ValueError("max_regions must be positive") + if cache_dir is not None and cache_dir.resolve().is_relative_to(repository.resolve()): + raise ValueError("cache directory must be outside the analyzed repository") started = time.monotonic() budget = ExecutionBudget(started + config.deadline_seconds, config.cancellation_allowance_seconds) scan_budget = ScanBudget(max_model_calls=0, max_regions=max_regions, order_by_evidence=True) if backend is None: backend = JoernBackend() + cache = None + cache_semantics = None + reused: list[ReusingBackend] = [] records: list[ScanRecord] = [] manifests: list[SnapshotManifest] = [] reasons: list[str] = [] @@ -179,16 +208,66 @@ def replay( stage, stage_started = "provenance", time.monotonic() provenance.update(_prepare(lambda: _provenance(repository, scan_budget), budget)) timings[stage + "_seconds"] = time.monotonic() - stage_started + if cache_dir is not None: + cache = ArtifactCache(cache_dir, max_bytes=config.cache_max_bytes) + cache_semantics = SemanticKeys( + provenance["facts"], + provenance["queries"], + digest_value({"query_parameters": "complete-joern-request-v1"}), + digest_value({"ranking_mode": "evidence", "options": asdict(scan_budget)}), + provenance["policy"], + provenance["model"], + provenance["capabilities"], + config.mode, + ) + provenance["cache"] = "validated_local_reuse" + discovery_identity = digest_value({"code": _prepare(lambda: _discovery_identity(), budget), "facts": provenance["facts"]}) stage, stage_started = "preparation", time.monotonic() with adapter.materialize(comparison.head_oid, budget=budget) as tip: manifests.append(tip.manifest) assert comparison.base_oid is not None - with adapter.materialize(comparison.base_oid, budget=budget) as old: + old_context = ( + nullcontext(tip) if comparison.base_oid == comparison.head_oid else adapter.materialize(comparison.base_oid, budget=budget) + ) + with old_context as old: manifests.append(old.manifest) for manifest in manifests: reasons.extend("snapshot:" + boundary.reason for boundary in manifest.boundaries) - head_regions, head_declarations = _prepare(lambda: _discover(tip.root), budget) - base_regions, base_declarations = _prepare(lambda: _discover(old.root), budget) + timings["snapshot_seconds"] = time.monotonic() - stage_started + timings["snapshot_reuses"] = int(comparison.base_oid == comparison.head_oid) + discovery_started = time.monotonic() + head_regions, head_declarations, head_hit = _prepare( + lambda: discovery(tip.root, tip.manifest, cache=cache, identity=discovery_identity, budget=budget, discover=_discover), + budget, + ) + base_regions, base_declarations, base_hit = _prepare( + lambda: discovery(old.root, old.manifest, cache=cache, identity=discovery_identity, budget=budget, discover=_discover), + budget, + ) + timings["discovery_seconds"] = time.monotonic() - discovery_started + timings["discovery_hits"] = int(head_hit) + int(base_hit) + head_backend = base_backend = backend + if ( + cache is not None + and cache_semantics is not None + and isinstance(backend, JoernBackend) + and all(m.complete for m in manifests) + ): + head_backend = ReusingBackend( + backend, + cache, + cache_semantics, + declarations=declaration_identity(tip.manifest, head_declarations), + exclusions=provenance["graph_layout"], + ) + base_backend = ReusingBackend( + backend, + cache, + cache_semantics, + declarations=declaration_identity(old.manifest, base_declarations), + exclusions=provenance["graph_layout"], + ) + reused.extend((head_backend, base_backend)) context = adapter.compare(comparison, declaration_paths=tuple(set(head_declarations + base_declarations)), budget=budget) context = replace(context, unresolved_boundaries=tuple(dict.fromkeys((*context.unresolved_boundaries, *reasons)))) reasons.extend(context.unresolved_boundaries) @@ -197,7 +276,7 @@ def replay( head_scan = scan_repository( tip.root, head_regions, - backend=backend, + backend=head_backend, budget=scan_budget, execution_budget=budget, ranking_mode="evidence", @@ -214,11 +293,13 @@ def replay( head_regions=head_regions, base_regions=base_regions, context=context, - backend=backend, + backend=base_backend, execution_budget=budget, scan_budget=scan_budget, head_semantics=provenance["semantics"], base_semantics=provenance["semantics"], + cache=cache, + cache_semantics=cache_semantics, ) if delta.base_scan is not None: records.append(ScanRecord(comparison, "base", delta.base_scan)) @@ -251,6 +332,8 @@ def replay( timings[stage + "_seconds"] = time.monotonic() - stage_started if time.monotonic() >= budget.deadline_monotonic: reasons.append("deadline_exhausted") + timings["graph_hits"] = sum(item.graph_hits for item in reused) + timings["query_hits"] = sum(item.query_hits for item in reused) head_records = [r.scan for r in records if r.side == "head"] for record in records: if record.scan.scope is not None: diff --git a/tests/test_graph_reuse.py b/tests/test_graph_reuse.py index 544e0ef..6b7010d 100644 --- a/tests/test_graph_reuse.py +++ b/tests/test_graph_reuse.py @@ -88,3 +88,21 @@ def test_generic_driver_accepts_backend_validated_lease(tmp_path, monkeypatch): assert result.run("census", {})["file_names"] == ["a.js"] _dispose(result) assert graph.read_bytes() == b"graph original" + + +def test_installed_frontend_adaptation_changes_graph_identity(tmp_path, monkeypatch): + import openultrasast.cpg.backend as module + + backend, root, _, identity, _ = prepared(tmp_path, monkeypatch) + home = tmp_path / "engine" + home.mkdir() + launcher = home / "joern" + launcher.write_text("launcher") + jar = home / "frontends/jssrc2cpg/lib/io.joern.jssrc2cpg-4.0.625.jar" + jar.parent.mkdir(parents=True) + jar.write_bytes(b"frontend-one") + monkeypatch.setattr(module.shutil, "which", lambda name: str(launcher) if name == "joern" else None) + first = backend.graph_identity(root, language="javascript", declarations_digest="d", exclusions_digest="e") + jar.write_bytes(b"frontend-two") + second = backend.graph_identity(root, language="javascript", declarations_digest="d", exclusions_digest="e") + assert first != second diff --git a/tests/test_push_reuse.py b/tests/test_push_reuse.py new file mode 100644 index 0000000..f611eb8 --- /dev/null +++ b/tests/test_push_reuse.py @@ -0,0 +1,89 @@ +"""Live integration of validated graph and query reuse under one deadline.""" + +import json +import time + +from openultrasast.cpg.backend import CpgResult, JoernBackend +from openultrasast.model.contracts import ExecutionBudget +from openultrasast.push.cache import ArtifactCache, SemanticKeys +from openultrasast.push.reuse import ReusingBackend + + +def test_backend_reuses_graph_and_answers_then_invalidates_source_and_dependencies(tmp_path, monkeypatch): + import openultrasast.cpg.backend as module + + monkeypatch.setattr(module, "joern_version", lambda: (4, 0, 625)) + builds = [] + queries = [] + + class Engine(JoernBackend): + def build(self, root, *, language, exclude, execution_budget): + builds.append(root) + path = tmp_path / f"graph-{len(builds)}.json" + paths = [str(p) for p in root.glob("*.js")] + path.write_text(json.dumps(paths)) + return CpgResult(path, lambda kind, params: self.query(path, kind, params), lambda kind, req: self.query_batch(path, kind, req)) + + def query(self, path, kind, params): + names = json.loads(path.read_text()) + return {"files": len(names), "methods": 2, "calls": 1, "file_names": names, "overlays": "dataflowOss"} + + def query_batch(self, path, kind, requests): + queries.append((kind, requests)) + return {**{rid: [] for rid in requests}, "__census__": [self.query(path, "census", {})]} + + root = tmp_path / "source" + root.mkdir() + (root / "a.js").write_text("function f() {}") + cache = ArtifactCache(tmp_path / "cache", max_bytes=100000) + semantics = SemanticKeys("facts", "queries", "config", "ranking", "admission", "disabled", "empty", "advisory") + deadline = ExecutionBudget(time.monotonic() + 10, 0.2) + backend = ReusingBackend(Engine(), cache, semantics, declarations="dependencies", exclusions="exclusions") + + def run(): + result = backend.build(root, language="javascript", exclude=(), execution_budget=deadline) + assert result is not None + answer = result.run_batch("taint", {"q": {"file": "a.js", "callDepth": 3}}) + if result.cleanup: + result.cleanup() + return answer + + expected = run() + assert run() == expected + assert len(builds) == 1 and len(queries) == 1 + (root / "a.js").write_text("function g() {}") + run() + assert len(builds) == 2 and len(queries) == 2 + backend.declarations = "changed-dependency" + run() + assert len(builds) == 3 and len(queries) == 3 + backend.semantics = SemanticKeys("changed-facts", "queries", "config", "ranking", "admission", "disabled", "empty", "advisory") + run() + assert len(builds) == 3 and len(queries) == 4 + + +def test_fresh_missing_census_cannot_borrow_a_cached_query_census(tmp_path): + from openultrasast.cpg.artifact import GraphArtifact, GraphCensus, GraphIdentity + + identity = GraphIdentity( + "source", "declarations", "exclude", "repository", "javascript", "engine", "frontend", "version", ("dataflowOss",), () + ) + artifact = GraphArtifact(identity, "graph-bytes", 10, GraphCensus(1, 1, 1), "complete", (), "/old", ("a.js",)) + cache = ArtifactCache(tmp_path / "cache", max_bytes=100000) + semantics = SemanticKeys("facts", "queries", "config", "ranking", "admission", "disabled", "empty", "advisory") + wrapper = ReusingBackend(JoernBackend(), cache, semantics, declarations="d", exclusions="e") + census = [{"files": 1, "methods": 1, "file_names": ["a.js"]}] + calls = [] + + def batch(kind, requests): + calls.append(requests) + result = {rid: [] for rid in requests} + if len(calls) == 1: + result["__census__"] = census + return result + + graph = CpgResult(tmp_path / "graph", lambda *_: None, batch) + result = wrapper._answers(graph, artifact, ExecutionBudget(time.monotonic() + 5, 0.2)) + assert "q" in result.run_batch("taint", {"q": {"file": "a.js"}}) + answer = result.run_batch("taint", {"q": {"file": "a.js"}, "fresh": {"file": "a.js", "callDepth": 3}}) + assert "q" in answer and "fresh" not in answer diff --git a/tests/test_push_runner.py b/tests/test_push_runner.py index f9ad498..0a5591a 100644 --- a/tests/test_push_runner.py +++ b/tests/test_push_runner.py @@ -218,3 +218,84 @@ def test_default_replay_does_not_launch_unbudgeted_availability_probe(tmp_path, monkeypatch.setattr(runner, "JoernBackend", Backend, raising=False) result = replay(root, base=base, head=head, artifact=tmp_path / "probe.json") assert builds and result.result.coverage_status == "incomplete" + + +def test_replay_persists_discovery_without_skipping_readable_snapshots(tmp_path, monkeypatch): + from openultrasast.push import runner + + root, base, head, _ = history(tmp_path) + cache = tmp_path / "cache" + cold = tmp_path / "cold.json" + warm = tmp_path / "warm.json" + replay(root, base=base, head=head, artifact=cold, backend=NullBackend(), cache_dir=cache) + + def should_reuse(*args): + raise AssertionError("discovery should be cached") + + monkeypatch.setattr(runner, "_discover", should_reuse) + replay(root, base=base, head=head, artifact=warm, backend=NullBackend(), cache_dir=cache) + a, b = json.loads(cold.read_text()), json.loads(warm.read_text()) + assert a["result"] == b["result"] + assert a["snapshots"] == b["snapshots"] + assert b["timings"]["discovery_hits"] == 2 + assert b["snapshots"][0]["bytes_read"] > 0 + + +def test_cache_cannot_write_inside_repository(tmp_path): + root, base, head, git = history(tmp_path) + before = git("status", "--porcelain") + with pytest.raises(ValueError, match="cache.*outside"): + replay(root, base=base, head=head, artifact=tmp_path / "out.json", cache_dir=root / "cache") + assert git("status", "--porcelain") == before + + +def test_cached_discovery_invalidates_source_and_dependency_edits(tmp_path): + root, base, head, git = history(tmp_path) + cache = tmp_path / "cache" + replay(root, base=base, head=head, artifact=tmp_path / "initial.json", backend=NullBackend(), cache_dir=cache) + (root / "api.js").write_text("function replacement(req) { eval(req.body.other); }") + git("add", "api.js") + git("commit", "-m", "change source") + edited = git("rev-parse", "HEAD") + replay(root, base=base, head=edited, artifact=tmp_path / "edited.json", backend=NullBackend(), cache_dir=cache) + data = json.loads((tmp_path / "edited.json").read_text()) + assert data["timings"]["discovery_hits"] == 1 + (root / "package.json").write_text('{"dependencies":{"express":"5"}}') + git("add", "package.json") + git("commit", "-m", "change dependencies") + dependency = git("rev-parse", "HEAD") + replay(root, base=base, head=dependency, artifact=tmp_path / "dependency.json", backend=NullBackend(), cache_dir=cache) + data = json.loads((tmp_path / "dependency.json").read_text()) + assert data["timings"]["discovery_hits"] == 1 + assert data["change_context"]["head_revision"] == dependency + + +def test_identical_revision_materializes_once_and_reuses_discovery(tmp_path, monkeypatch): + from openultrasast.push.snapshot import SnapshotAdapter + + root, base, _, _ = history(tmp_path) + original = SnapshotAdapter.materialize + calls = [] + + def counted(self, oid, **kwargs): + calls.append(oid) + return original(self, oid, **kwargs) + + monkeypatch.setattr(SnapshotAdapter, "materialize", counted) + replay(root, base=base, head=base, artifact=tmp_path / "same.json", backend=NullBackend(), cache_dir=tmp_path / "cache") + data = json.loads((tmp_path / "same.json").read_text()) + assert calls == [base] + assert data["timings"]["discovery_hits"] == 1 + assert len(data["snapshots"]) == 2 + + +def test_admission_provenance_invalidates_when_installed_runtime_changes(tmp_path, monkeypatch): + from openultrasast.model.scan import ScanBudget + from openultrasast.push import runner + + monkeypatch.setattr(runner, "engine_runtime_identity", lambda: "first-installation") + first = runner._provenance(tmp_path, ScanBudget()) + monkeypatch.setattr(runner, "engine_runtime_identity", lambda: "second-installation") + second = runner._provenance(tmp_path, ScanBudget()) + assert first["semantics"] != second["semantics"] + assert first["facts"] == second["facts"] and first["graph_layout"] == second["graph_layout"]