diff --git a/.github/osv-scanner-frozen-wsl-retrieval.toml b/.github/osv-scanner-frozen-wsl-retrieval.toml new file mode 100644 index 000000000..ccd92e1f9 --- /dev/null +++ b/.github/osv-scanner-frozen-wsl-retrieval.toml @@ -0,0 +1,12 @@ +# Dedicated OSV-Scanner v2.6.0 configuration for the retired historical WSL retrieval lock only. +# The explicit --config applies to every input of its invocation, not to a path encoded in this file: +# https://github.com/google/osv-scanner/blob/v2.6.0/docs/configuration.md +# https://github.com/google/osv-scanner/blob/v2.6.0/internal/config/manager.go (Manager.Get). +# The workflow's three disjoint input lists and preflight policy tests enforce the exact caller scope, +# reviewed digest, evidence, retirement guard and expiry. No ordinary or active QMD input receives this config. +# Its native bare-date syntax matches the unchanged main564 macOS archive pattern and the retained root control. + +[[IgnoredVulns]] +id = "GHSA-vfj7-8cjw-p6xm" +ignoreUntil = 2026-10-17 +reason = "2026-10-03 bounded retirement review: this config is assigned only to blueprints/convergence-practice/wsl-retrieval/package-lock.json at SHA256 5c51ee65cc477f2c1488a38ff5cad1c0a737f81a5b61bbd70d5edc4d15bfc3bb. It preserves the original lock as historical evidence; braces 3.0.3 remains affected by GHSA-vfj7-8cjw-p6xm/CVE-2026-93687. Both old source/QMD runner modes stop before subprocess or output creation, and a dependency-free private manifest uses npm 11.19.0 devEngines.runtime.name=retired-wsl-retrieval/onFail=error to reject supported install/ci entry points. Original manifest/recording-aid bytes are archived and original receipts, failure facts and prior runner archives remain unchanged. This is a supported-entry-point guard, not an installation sandbox: explicit --force, other package managers and restored historical files are outside its claim. Source disposition and exact artifact bindings: blueprints/convergence-practice/wsl-retrieval/retirement-assessment.json. Independent native controls and retained failures: evidence/receipts/wsl-retrieval-retirement-20261003.json. Owner handoff: https://github.com/seathatflowsinourveins/native-agent-stack/pull/608#issuecomment-5963764481. The workflow and inventory tests constrain this one input; OSV itself does not constrain an explicit config to that path. Active QMD remains separately unresolved and ordinary/macOS configs carry no exception for this advisory. This exception expires at the native TOML date 2026-10-17; retirement does not patch a dependency or qualify active QMD." diff --git a/.github/osv-scanner-lockfiles.json b/.github/osv-scanner-lockfiles.json index 26d3e8049..51dc7ba36 100644 --- a/.github/osv-scanner-lockfiles.json +++ b/.github/osv-scanner-lockfiles.json @@ -24,7 +24,8 @@ "path": "blueprints/convergence-practice/application-delivery/uv.lock" }, { - "path": "blueprints/convergence-practice/wsl-retrieval/package-lock.json" + "path": "blueprints/convergence-practice/wsl-retrieval/package-lock.json", + "config": ".github/osv-scanner-frozen-wsl-retrieval.toml" }, { "path": "blueprints/memory-stack/native-qualification/uv.lock" diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index 7bfe408cd..6e0b5e7e7 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -4,9 +4,10 @@ name: Dependency and workflow security scan # .github/osv-scanner-lockfiles.json (tests/test_osv_lockfile_coverage.py fails # when a tracked one is missing; its "excluded" list names deliberately vulnerable # test fixtures, each with a reason) and fails on any vulnerability not ignored in -# .github/osv-scanner.toml, or, for the entries that name .github/osv-scanner-frozen-macos.toml, in that -# config (an explicit --config applies to every input of one invocation, so those entries are scanned on -# their own); its pull_request run is a required check (branch ruleset +# .github/osv-scanner.toml, or the dedicated frozen macOS and retired WSL configs named by those entries. +# An explicit --config applies to every input of one invocation; the workflow and policy preflight bind +# each dedicated config to its exact reviewed lock. Three exhaustive/disjoint groups are scanned separately; +# its pull_request run is a required check (branch ruleset # 23739774). The scan jobs hold only a read token: on push, schedule and dispatch # osv-sarif-upload and zizmor-sarif-upload, which run no installed tool, upload the SARIF. # zizmor-online adds zizmor's online audits as SARIF for code scanning; since @@ -64,47 +65,85 @@ jobs: WRITE_SARIF: ${{ github.event_name != 'pull_request' }} run: | # `shell: bash` runs with -e; turn it off so a findings exit (1) is captured and - # the SARIF is still written, then fail the step with the worst status of the table runs. + # all three SARIF results are still written, then fail with the worst observed status. set +e -u -o pipefail inventory=.github/osv-scanner-lockfiles.json frozen_config=.github/osv-scanner-frozen-macos.toml + frozen_wsl_config=.github/osv-scanner-frozen-wsl-retrieval.toml + # Scope comes from this caller, not OSV's explicit-config mechanism. Reject missing/duplicate inputs, + # config drift, changed lock digests, missing evidence, expired policy and restored replay/install routes. + python3 -m unittest \ + tests.test_osv_lockfile_coverage.LockfileInventoryTests \ + tests.test_osv_lockfile_coverage.FrozenScanTests \ + tests.test_wsl_retrieval.RetiredRunnerTests || exit "$?" # An entry without a parser lets OSV-Scanner infer it from the file name (":"). # An explicit --config applies to every input of one invocation (OSV-Scanner docs/configuration.md), - # so the entries that name the frozen config are scanned in an invocation of their own under that - # config alone, and every other entry under .github/osv-scanner.toml, which holds no exception for them. - mapfile -t lockfiles < <(jq -r '.lockfiles[] | select(has("config") | not) | "--lockfile=\(.parser // ""):\(.path)"' "$inventory") - mapfile -t frozen < <(jq -r --arg config "$frozen_config" '.lockfiles[] | select(.config == $config) | "--lockfile=\(.parser // ""):\(.path)"' "$inventory") + # so each frozen group has its own invocation, and ordinary entries use the config with no archive exception. + # Read in the current shell without relying on mapfile, which older Bash lacks. + lockfiles=() + while IFS= read -r lockfile; do + lockfiles+=("$lockfile") + done < <(jq -r '.lockfiles[] | select(has("config") | not) | "--lockfile=\(.parser // ""):\(.path)"' "$inventory") + frozen=() + while IFS= read -r lockfile; do + frozen+=("$lockfile") + done < <(jq -r --arg config "$frozen_config" '.lockfiles[] | select(.config == $config) | "--lockfile=\(.parser // ""):\(.path)"' "$inventory") + frozen_wsl=() + while IFS= read -r lockfile; do + frozen_wsl+=("$lockfile") + done < <(jq -r --arg config "$frozen_wsl_config" '.lockfiles[] | select(.config == $config) | "--lockfile=\(.parser // ""):\(.path)"' "$inventory") + # Older Bash treats empty arrays as unset under -u; guard before expanding lengths. + test -n "${lockfiles[0]+set}" || exit 1 + test -n "${frozen[0]+set}" || exit 1 + test -n "${frozen_wsl[0]+set}" || exit 1 test "${#lockfiles[@]}" -gt 0 || exit 1 test "${#frozen[@]}" -gt 0 || exit 1 - # Every entry is in exactly one scan: together the two lists are the inventory, so an entry that + test "${#frozen_wsl[@]}" -gt 0 || exit 1 + # Every entry is in exactly one scan: together the three lists are the inventory, so an entry that # names any other config is in neither and fails here. - test "$(( ${#lockfiles[@]} + ${#frozen[@]} ))" -eq "$(jq '.lockfiles | length' "$inventory")" || exit 1 + test "$(( ${#lockfiles[@]} + ${#frozen[@]} + ${#frozen_wsl[@]} ))" -eq "$(jq '.lockfiles | length' "$inventory")" || exit 1 + # Also enforce unique inputs and the two exact archive assignments in the shell caller itself. + jq -e --arg mac_config "$frozen_config" --arg wsl_config "$frozen_wsl_config" ' + .lockfiles as $entries | ($entries | map(.path)) as $paths | + (($paths | length) == ($paths | unique | length)) and + ([$entries[] | select(.config == $mac_config) | .path] == + ["evidence/artifacts/macos-application-20260924/variant/pnpm-lock.yaml"]) and + ([$entries[] | select(.config == $wsl_config) | .path] == + ["blueprints/convergence-practice/wsl-retrieval/package-lock.json"]) + ' "$inventory" > /dev/null || exit 1 # --no-resolve: scan the versions each file pins. Transitive resolution of the # unlocked manifests reported versions no lockfile installs (decision record). scan=("$RUNNER_TEMP/osv-scanner/osv-scanner" scan source --config .github/osv-scanner.toml --no-resolve "${lockfiles[@]}") scan_frozen=("$RUNNER_TEMP/osv-scanner/osv-scanner" scan source --config "$frozen_config" --no-resolve "${frozen[@]}") + scan_frozen_wsl=("$RUNNER_TEMP/osv-scanner/osv-scanner" scan source --config "$frozen_wsl_config" + --no-resolve "${frozen_wsl[@]}") "${scan[@]}" status=$? "${scan_frozen[@]}" frozen_status=$? + "${scan_frozen_wsl[@]}" + frozen_wsl_status=$? + statuses=("$status" "$frozen_status" "$frozen_wsl_status") + printf 'OSV primary exit codes: ordinary=%s frozen-macos=%s frozen-wsl=%s\n' "$status" "$frozen_status" "$frozen_wsl_status" if [ "$WRITE_SARIF" = true ]; then "${scan[@]}" --format sarif --output-file "$RUNNER_TEMP/osv-scanner/osv-scanner.sarif" sarif_status=$? "${scan_frozen[@]}" --format sarif --output-file "$RUNNER_TEMP/osv-scanner/osv-scanner-frozen-macos.sarif" frozen_sarif_status=$? + "${scan_frozen_wsl[@]}" --format sarif --output-file "$RUNNER_TEMP/osv-scanner/osv-scanner-frozen-wsl-retrieval.sarif" + frozen_wsl_sarif_status=$? + statuses+=("$sarif_status" "$frozen_sarif_status" "$frozen_wsl_sarif_status") + printf 'OSV SARIF exit codes: ordinary=%s frozen-macos=%s frozen-wsl=%s\n' "$sarif_status" "$frozen_sarif_status" "$frozen_wsl_sarif_status" # 0: no vulnerabilities; 1: vulnerabilities (already reported above); other codes are scanner errors. - for code in "$sarif_status" "$frozen_sarif_status"; do - if [ "$code" -gt 1 ]; then - exit "$code" - fi - done - fi - # The worst status of the two scans wins: 0 clean, 1 vulnerabilities, more a scanner error. - if [ "$frozen_status" -gt "$status" ]; then - status=$frozen_status fi + # Preserve the worst status from every primary and SARIF invocation, including findings and scanner errors. + for code in "${statuses[@]}"; do + if [ "$code" -gt "$status" ]; then + status=$code + fi + done exit "$status" - name: Keep the OSV-Scanner SARIF for the upload job # !cancelled(): a findings exit fails the scan step, and its SARIF must still reach @@ -116,6 +155,7 @@ jobs: path: | ${{ runner.temp }}/osv-scanner/osv-scanner.sarif ${{ runner.temp }}/osv-scanner/osv-scanner-frozen-macos.sarif + ${{ runner.temp }}/osv-scanner/osv-scanner-frozen-wsl-retrieval.sarif if-no-files-found: error retention-days: 1 @@ -152,6 +192,11 @@ jobs: with: sarif_file: ${{ runner.temp }}/osv-scanner/osv-scanner-frozen-macos.sarif category: osv-scanner-frozen-macos + - name: Upload the retired WSL artifact OSV-Scanner SARIF to code scanning + uses: github/codeql-action/upload-sarif@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1 + with: + sarif_file: ${{ runner.temp }}/osv-scanner/osv-scanner-frozen-wsl-retrieval.sarif + category: osv-scanner-frozen-wsl-retrieval zizmor-online: if: github.event_name != 'pull_request' diff --git a/blueprints/convergence-practice/wsl-retrieval/README.md b/blueprints/convergence-practice/wsl-retrieval/README.md index d9643a466..4be7d4b2f 100644 --- a/blueprints/convergence-practice/wsl-retrieval/README.md +++ b/blueprints/convergence-practice/wsl-retrieval/README.md @@ -1,4 +1,18 @@ -# Incomplete historical WSL retrieval reference +# Retired historical WSL retrieval reference + +This directory is retired for installation and source/QMD replay. [run.py](run.py) +fails both old modes before launching a subprocess or creating an output directory. +The dependency-free [package.json](package.json) is a retirement guard; the original +manifest and recording aid are preserved as text artifacts. The retained lock +stays at its original path and remains in scanner inventory. Retirement neither +patches the dependency nor establishes a scanner exception. + +The separate current QMD recipe is in [recipes/README.md](../../../recipes/README.md#component-catalog-install-and-check), +with current native fixture guidance in [docs/native-token-ci.md](../../../docs/native-token-ci.md). +Active QMD's [GHSA-vfj7-8cjw-p6xm](https://github.com/advisories/GHSA-vfj7-8cjw-p6xm) +advisory remains unresolved; this historical retirement does not qualify or change +that setup. [retirement-assessment.json](retirement-assessment.json) records the +current source disposition separately from the original receipts. The retained September 20, 2026 receipts are an **incomplete historical fixture reference**. They do not establish native E2E or adoption acceptance. Per-command @@ -45,6 +59,17 @@ The successful QMD receipt originally mapped `run.py` directly; the offline audi now resolves that historical name to the archived bytes. Neither receipt was rewritten to pretend it recorded today's file or missing invocation metadata. +[package-original.json.txt](package-original.json.txt) preserves the original +174-byte manifest with SHA-256 +`7bbf63c5eafd347ae5ae56c684be06ef2589d38f2aab580ca7986ca4122bc6a8`. +Every historical `package.json` binding resolves to that archive without rewriting +the receipts' frozen-input names or declared runner mappings. +[run-recording-aid.py.txt](run-recording-aid.py.txt) preserves the later 16,427-byte +recording aid with SHA-256 +`be852ce99501f5bc4567b846b90fb0e91d91de77b0eafbd3b72bb4484a2f7d12`. +It was a future recording aid, and is not attributed to the original execution. +All original receipts, source review and earlier runner archives remain unchanged. + The [install receipt](install-receipt.json) still records the actual historical `npm ci --ignore-scripts --omit=optional` action. Its contents and digest are unchanged. The [inventory](install-inventory.json) reports no optional llama @@ -78,35 +103,30 @@ omitted inputs and same-count replacement checks as well as incorrect spans, source bodies, URI scope, stale updates, erased failures and unsupported claims. A zero audit exit means retained facts are consistent; its result explicitly reports `native_acceptance_established: false`. It cannot repair missing evidence. - -## Future command capture and supported installation - -[run.py](run.py) is a future recording aid. It now retains each attempted command's -sanitized argument vector and working directory before launch, preserves argument -boundaries, and records failed launches and timeouts. Private path roots become -scope markers such as `` and ``; original historical facts receive no -fabricated fields. Mocked regressions exercise this recording without invoking -native retrieval. Both previously executed runner versions remain archived. - -For a separately authorized fresh QMD trial, follow the maintained -[QMD native recipe](../../../recipes/README.md#component-catalog-install-and-check) and -[native token fixture guidance](../../../docs/native-token-ci.md). The supported -installation control is: - -```sh -NODE_LLAMA_CPP_SKIP_DOWNLOAD=true npm install --global \ - --prefix "$NEW_OWNED_QMD_PREFIX" @tobilu/qmd@2.8.3 -``` - -Required npm dependency lifecycle scripts remain enabled. Do not reuse the -historical blanket `--ignore-scripts` command as a fresh-host recipe. The retained -lock and optional-backend assertions describe the historical prefix; they do not -prove compatibility of a newly installed prefix. Use the maintained native fixture -for current supported installation acceptance, retaining its actual install and -runtime commands and outputs. This review performs no install or native rerun. - -Acceptance requires recovered verifiable historical invocation evidence or a -separately authorized reachable-host trial with supported installation and complete -command capture. Preserve native accounts, model/effort settings, caching and -compaction. No automatic history capture is introduced. Whole-task provider, -parent/child/retry/cache usage remains unknown; no savings claim is made. +The companion `current_retirement_assessment` checks the exact new archives, +retained lock, retirement manifest and current entrypoint hashes. Regressions reject +changed recording-aid or manifest archives, restored dependencies/scripts and a +missing runtime guard. Both old modes must stop before output or subprocess work. +These are local integration and artifact checks; they do not establish native npm +guard acceptance or a scanner exception. + +## Supported entrypoint retirement + +The retained private manifest has no dependencies or lifecycle/replay scripts. Its +`devEngines.runtime` names `retired-wsl-retrieval` with `onFail: error`. In the reviewed +[npm 11.19.0 supported behavior](https://github.com/npm/cli/blob/v11.19.0/lib/base-cmd.js#L201), +that runtime name is rejected before ordinary `install` and `ci`, including +`--ignore-scripts`. The [tagged manifest documentation](https://github.com/npm/cli/blob/v11.19.0/docs/lib/content/configuring-npm/package-json.md#L1117) +describes the check. Independent native controls belong to the separate retirement +acceptance record; the offline audit checks the guard's artifacts only. + +Removing a manifest alone would leave [npm Arborist's root-lock fallback](https://github.com/npm/cli/blob/v11.19.0/workspaces/arborist/lib/arborist/load-virtual.js#L50). +The guard protects supported npm entry points. It is not an installation sandbox: +explicit `--force`, other package managers and restored historical files are +outside its claim. The text archives are evidence for offline review; this +directory provides no supported replay or installation route. + +Recovered historical invocation evidence could reopen the incomplete acceptance +assessment. A current QMD trial belongs to the separate maintained recipe and +current status, with supported installation and complete command capture. Whole-task +provider, parent/child/retry/cache usage remains unknown; no savings claim is made. diff --git a/blueprints/convergence-practice/wsl-retrieval/audit.py b/blueprints/convergence-practice/wsl-retrieval/audit.py index b5879df2b..d6d4a1816 100644 --- a/blueprints/convergence-practice/wsl-retrieval/audit.py +++ b/blueprints/convergence-practice/wsl-retrieval/audit.py @@ -60,8 +60,8 @@ def audit(source, qmd, failed, inventory, root=ROOT): (failed, {'run.py': 'run-initial.py.txt'}, 'run-initial.py.txt'), (qmd, {}, 'run-qmd-attempt-2.py.txt')]: require(receipt['frozen_file_mapping'] == declared_mapping, 'wrong executed runner provenance') - # The original QMD receipt had no remapping. Its run.py bytes are now archived. - mapping = {'run.py': runner} + # Resolve archived bytes without fabricating original receipt mapping fields. + mapping = {'run.py': runner, 'package.json': 'package-original.json.txt'} require(set(receipt['frozen_inputs']) == REQUIRED_FROZEN_INPUTS, 'required frozen input names changed') for name, digest in receipt['frozen_inputs'].items(): require(hashlib.sha256((root/mapping.get(name, name)).read_bytes()).hexdigest() == digest, @@ -163,7 +163,53 @@ def query(label, text, path, body=None): 'whole_task_provider_usage': None, 'semantic_rag_or_client_integration': False} +def audit_retirement(root=ROOT): + """Check current retirement artifacts without qualifying historical execution.""" + assessment = json.loads((root/'retirement-assessment.json').read_text()) + archives = { + 'package-original.json.txt': (174, '7bbf63c5eafd347ae5ae56c684be06ef2589d38f2aab580ca7986ca4122bc6a8'), + 'run-recording-aid.py.txt': (16427, 'be852ce99501f5bc4567b846b90fb0e91d91de77b0eafbd3b72bb4484a2f7d12'), + } + require(set(assessment['archived_artifacts']) == set(archives), 'retirement archive names changed') + for name, (size, digest) in archives.items(): + data = (root/name).read_bytes() + require(len(data) == size and hashlib.sha256(data).hexdigest() == digest, + 'changed retirement artifact: '+name) + require(assessment['archived_artifacts'][name]['bytes'] == size + and assessment['archived_artifacts'][name]['sha256'] == digest, + 'retirement archive identity changed: '+name) + require(assessment['retained_lock']['path'] == 'package-lock.json' + and assessment['retained_lock']['sha256'] == '5c51ee65cc477f2c1488a38ff5cad1c0a737f81a5b61bbd70d5edc4d15bfc3bb' + and hashlib.sha256((root/'package-lock.json').read_bytes()).hexdigest() + == assessment['retained_lock']['sha256'], 'retired lock changed') + manifest = json.loads((root/'package.json').read_text()) + require(manifest.get('private') is True, 'retirement manifest must remain private') + require(not any(manifest.get(name) for name in ['dependencies', 'devDependencies', + 'optionalDependencies', 'peerDependencies', 'bundledDependencies', 'bundleDependencies']), + 'retirement manifest restores dependencies') + require(not manifest.get('scripts'), 'retirement manifest restores lifecycle or replay scripts') + require(manifest.get('devEngines', {}).get('runtime') + == {'name': 'retired-wsl-retrieval', 'onFail': 'error'}, + 'retirement manifest runtime guard changed') + require(set(assessment['current_entrypoints']) == {'run.py', 'package.json'}, + 'retirement entrypoint set changed') + for name, identity in assessment['current_entrypoints'].items(): + require(hashlib.sha256((root/name).read_bytes()).hexdigest() == identity['sha256'], + 'changed retirement entrypoint: '+name) + require(assessment['historical_native_acceptance_established'] is False + and assessment['native_npm_guard_acceptance_established'] is False, + 'offline retirement assessment cannot establish native acceptance') + require(assessment['active_qmd_advisory'] + == {'id': 'GHSA-vfj7-8cjw-p6xm', 'status': 'unresolved'}, + 'active QMD advisory disposition changed') + return {'artifacts_consistent': True, 'status': 'retired_historical_source', + 'native_npm_guard_acceptance_established': False, + 'active_qmd_advisory_status': 'unresolved'} + + if __name__ == '__main__': load = lambda name: json.loads((ROOT/name).read_text()) - print(json.dumps(audit(load('source-receipt.json'), load('qmd-receipt.json'), - load('qmd-attempt-1.json'), load('install-inventory.json')), indent=2)) + result = audit(load('source-receipt.json'), load('qmd-receipt.json'), + load('qmd-attempt-1.json'), load('install-inventory.json')) + result['current_retirement_assessment'] = audit_retirement() + print(json.dumps(result, indent=2)) diff --git a/blueprints/convergence-practice/wsl-retrieval/experiment.json b/blueprints/convergence-practice/wsl-retrieval/experiment.json index 68108d649..34cefa6da 100644 --- a/blueprints/convergence-practice/wsl-retrieval/experiment.json +++ b/blueprints/convergence-practice/wsl-retrieval/experiment.json @@ -3,7 +3,7 @@ "kind": "convergence_experiment", "status": "observed", "lane": "retrieval", - "task_and_failure": "Audit the incomplete historical September 20 WSL fixture reference offline. Missing per-command argv/cwd, unavailable raw-log locations and unsupported blanket lifecycle-script suppression prevent native E2E or adoption acceptance.", + "task_and_failure": "Retire supported source/QMD replay and installation entry points for the incomplete historical September 20 WSL fixture while preserving offline audit and original evidence. Missing per-command argv/cwd, unavailable raw-log locations and unsupported blanket lifecycle-script suppression still prevent native E2E or adoption acceptance.", "primary_sources_and_pins": [ { "url": "https://github.com/BurntSushi/ripgrep/releases/tag/15.2.0", @@ -20,18 +20,35 @@ { "url": "https://github.com/giancarloerra/SocratiCode/releases/tag/v1.14.0", "revision": "2218f25153d0f3f4a76ee240a5643dbc873e80be" + }, + { + "url": "https://github.com/npm/cli/blob/v11.19.0/docs/lib/content/configuring-npm/package-json.md#L1117", + "revision": "bfacd33ccbcd908480610703b60455d2da5b57a9" + }, + { + "url": "https://github.com/npm/cli/blob/v11.19.0/lib/base-cmd.js#L201", + "revision": "bfacd33ccbcd908480610703b60455d2da5b57a9" + }, + { + "url": "https://github.com/npm/cli/blob/v11.19.0/workspaces/arborist/lib/arborist/load-virtual.js#L50", + "revision": "bfacd33ccbcd908480610703b60455d2da5b57a9" + }, + { + "url": "https://github.com/seathatflowsinourveins/native-agent-stack/blob/56473e4b840f0e6940c031801d866e7e9bf29baf/blueprints/convergence-practice/wsl-retrieval/audit.py", + "revision": "56473e4b840f0e6940c031801d866e7e9bf29baf" } ], "discovery_provenance": [ - "Existing selected ripgrep, ast-grep and QMD components; bounded official-source preflight dated September 20. No discovery-list installation or fresh currency claim during offline completion." + "Existing selected ripgrep, ast-grep and QMD components; bounded official-source preflight dated September 20. No discovery-list installation or fresh currency claim during offline completion.", + "The accepted bounded retirement design reuses the main564 archival audit and npm/cli v11.19.0 devEngines/runtime and standalone-lock source review. No retrieval, npm installation, provider run or scanner exception is established by this offline assessment." ], "license_and_compatibility": "Dated pins and tagged license identities are preserved: ripgrep MIT OR Unlicense; ast-grep and QMD MIT. The retained Linux x86_64 fixture reports Node 24.21.0, QMD 2.8.3, better-sqlite3 13.0.3 and sqlite-vec 0.1.9. Historical npm --ignore-scripts installation is not a supported fresh-host recipe; native dependency setup is not qualified.", "baseline_and_candidate": { "baseline": "Frozen Python string/AST oracles and explicit source bodies; no performance or model baseline run.", - "candidate": "Offline consistency audit of selected facts from incomplete historical receipts; no independently verified native invocation." + "candidate": "Offline consistency audit of selected facts from incomplete historical receipts, with current supported replay/install entry points retired; no independently verified historical native invocation." }, "frozen_inputs": { - "base_revision": "6f74bc503ccecaaf6ccebd53677b23aedab50921", + "base_revision": "56473e4b840f0e6940c031801d866e7e9bf29baf", "tasks": [ "historical-reference-audit" ], @@ -47,6 +64,10 @@ "path": "blueprints/convergence-practice/wsl-retrieval/run-qmd-attempt-2.py.txt", "sha256": "4cbcceac02158629ca78262aaa826c995c21bbe45fa83481f7c139f16a6c52d2" }, + { + "path": "blueprints/convergence-practice/wsl-retrieval/run-recording-aid.py.txt", + "sha256": "be852ce99501f5bc4567b846b90fb0e91d91de77b0eafbd3b72bb4484a2f7d12" + }, { "path": "blueprints/convergence-practice/wsl-retrieval/seed/planner.py", "sha256": "3a33c48346221e2a103d4fb89d5675d2edaa97e651b7f08cf3a79b7ce0b8d7ef" @@ -58,7 +79,7 @@ "sha256": "19572d05638badc6a79abf392924261836e08b19bd01231372e2f764502d393d" }, { - "path": "blueprints/convergence-practice/wsl-retrieval/package.json", + "path": "blueprints/convergence-practice/wsl-retrieval/package-original.json.txt", "sha256": "7bbf63c5eafd347ae5ae56c684be06ef2589d38f2aab580ca7986ca4122bc6a8" }, { @@ -101,20 +122,28 @@ }, { "path": "blueprints/convergence-practice/wsl-retrieval/audit.py", - "sha256": "46cbcaecafe6d4bba3b09a26aa9770667eefbc26d7bbb806d4f0ab98078e86fd" + "sha256": "da2c4d58d7012febb1ce70d49111ecdbf22f46867fe84c098e01c75c7af0eabd" }, { "path": "tests/test_wsl_retrieval.py", - "sha256": "83353f3ccaa8bc01518f529846bf92167f7e21023e430ef8523af3e129afd903" + "sha256": "b4e1abcc70ed75d302ca3ecabfba23bda9f53ab4a460a1d0fc16fd47453ea7fa" }, { "path": "blueprints/convergence-practice/wsl-retrieval/run.py", - "sha256": "be852ce99501f5bc4567b846b90fb0e91d91de77b0eafbd3b72bb4484a2f7d12" + "sha256": "0df1c6873b8514cb08af2efebe479dfe46c35ad1e6b05d73796bee0d7cca702e" + }, + { + "path": "blueprints/convergence-practice/wsl-retrieval/package.json", + "sha256": "69ed6c37f8517e712ce64e1550c363926bdf25e42d8bfb30efa02b71197f3485" + }, + { + "path": "blueprints/convergence-practice/wsl-retrieval/retirement-assessment.json", + "sha256": "88f1919546dcf8b9fda7b425c7d3ea2ea114b975ccdc6356e0775a3f4e81a9f0" } ] }, "predeclared_metrics": { - "quality_rule": "Retrospective offline audit requires exact frozen-input and check-name sets, byte identities, source spans, QMD bodies, URI scope and retained failure. Consistency cannot establish the missing argv/cwd or independently certify historical execution. Native acceptance remains incomplete.", + "quality_rule": "Retrospective offline audit requires exact frozen-input and check-name sets, archived original manifest/runner identities, source spans, QMD bodies, URI scope and retained failure. A separate current retirement assessment checks the new archives, retained lock and dependency-free guard/diagnostic bindings. Consistency cannot establish missing argv/cwd, historical execution, native npm guard acceptance or scanner eligibility. Native historical acceptance remains incomplete.", "usage_rule": "The offline audit invokes no model. Historical per-fixture zero-usage declarations are retained as reported, not independently verified; whole-task parent/child/retry/cache accounting is unknown. No savings claim." }, "runtime_and_platform": { @@ -208,20 +237,22 @@ "limitations": [ "Incomplete historical fixture reference only; no native E2E or adoption acceptance. Receipt-declared source success (22 checks/six commands), QMD attempt-2 success (70 checks/17 commands), and failed QMD attempt 1 (five commands) are preserved without inventing missing invocation details.", "Exact frozen-input/check-name sets and selected result bytes can be audited offline. Historical argv/cwd are absent; original raw-log locations cannot be verified, so output digests do not prove which native command produced them.", - "Original install-receipt.json still records npm --ignore-scripts --omit=optional. This historical action is not a recommended installation recipe; future setup follows recipes/README.md and docs/native-token-ci.md with NODE_LLAMA_CPP_SKIP_DOWNLOAD=true and required dependency lifecycle scripts enabled.", - "Both historical runner versions remain byte-identical. The new run.py records sanitized argv/cwd and is a future recording aid, not evidence of a new execution or a repair of historical receipts.", + "Original install-receipt.json still records npm --ignore-scripts --omit=optional. This historical action is not a recommended installation recipe. This directory is retired; the separate current QMD recipe/status in recipes/README.md and docs/native-token-ci.md governs current setup.", + "Both historical runner versions remain byte-identical. The later recording aid and original package manifest are now byte-exact text archives. Historical manifest/runner resolution leaves every original receipt key/check set and mapping intact. The current run.py rejects both old modes before subprocess/output work.", "The first QMD attempt failed its bare-URI comparison while returning the expected body. The second runner corrected named-index URI parsing. Both receipts and all reported command counts remain preserved.", "Local synthetic fixtures do not qualify upstream suites, semantic RAG, production corpora, native clients, services, inference, OS confinement or resource performance.", "Historical verification.json records the earlier 18-test offline completion and its then-current assessment; it is not refreshed acceptance. Review changes supersede its acceptance wording without altering historical bytes.", - "Whole-task provider usage remains unknown. No savings claim, model/global change or automatic history capture." + "Whole-task provider usage remains unknown. No savings claim, model/global change or automatic history capture.", + "The current dependency-free package.json uses npm 11.19.0's devEngines runtime-name failure for supported install/ci entry points. Removing a manifest alone is insufficient because Arborist can load root metadata from a standalone lock. This guard is not an installation sandbox and does not cover explicit --force, other package managers or restored historical files. Native npm controls and scanner eligibility remain separate evidence.", + "Active QMD's GHSA-vfj7-8cjw-p6xm advisory remains unresolved. Retiring this historical lock does not patch a dependency, change the active QMD setup or establish a dedicated scanner exception." ], "decision_and_scope": { "decision": "defer", - "scope": "Incomplete historical fixture reference; native acceptance unavailable", + "scope": "Retired incomplete historical fixture reference; native acceptance unavailable", "qualification_run_ids": [] }, - "rollback": "No production or global deployment. Retain required evidence, then retire only the owned private fixture/index/package prefix if no longer needed; never remove or stop unrelated state or processes.", - "next_decision_changing_test": "Recover and verify the original raw logs and actual invocation provenance, or authorize a separate reachable-host native trial using the supported QMD installation recipe and complete command capture. Neither is performed by this offline repair.", + "rollback": "No production or global deployment. Original evidence and source bytes remain preserved; source retirement is recoverable through version control. No host state, services, accounts or unrelated processes are changed.", + "next_decision_changing_test": "Recovered verifiable historical invocation evidence could reopen historical acceptance. A current native QMD trial belongs to the separate maintained recipe/status. Independent native npm controls and exact-lock scanner review govern archival disposition; this offline experiment establishes neither.", "usage_assessment": { "claim": "none", "coverage": "partial", diff --git a/blueprints/convergence-practice/wsl-retrieval/package-original.json.txt b/blueprints/convergence-practice/wsl-retrieval/package-original.json.txt new file mode 100644 index 000000000..177e32716 --- /dev/null +++ b/blueprints/convergence-practice/wsl-retrieval/package-original.json.txt @@ -0,0 +1,9 @@ +{ + "name": "wsl-retrieval-qualification", + "version": "1.0.0", + "private": true, + "dependencies": { + "@tobilu/qmd": "2.8.3", + "sqlite-vec-linux-x64": "0.1.9" + } +} diff --git a/blueprints/convergence-practice/wsl-retrieval/package.json b/blueprints/convergence-practice/wsl-retrieval/package.json index 177e32716..6b45c2077 100644 --- a/blueprints/convergence-practice/wsl-retrieval/package.json +++ b/blueprints/convergence-practice/wsl-retrieval/package.json @@ -1,9 +1,12 @@ { - "name": "wsl-retrieval-qualification", + "name": "retired-wsl-retrieval", "version": "1.0.0", "private": true, - "dependencies": { - "@tobilu/qmd": "2.8.3", - "sqlite-vec-linux-x64": "0.1.9" + "description": "Retired historical WSL retrieval fixture; retained lock is audit evidence.", + "devEngines": { + "runtime": { + "name": "retired-wsl-retrieval", + "onFail": "error" + } } } diff --git a/blueprints/convergence-practice/wsl-retrieval/retirement-assessment.json b/blueprints/convergence-practice/wsl-retrieval/retirement-assessment.json new file mode 100644 index 000000000..b2e814223 --- /dev/null +++ b/blueprints/convergence-practice/wsl-retrieval/retirement-assessment.json @@ -0,0 +1,79 @@ +{ + "schema_version": 1, + "kind": "historical_source_retirement_assessment", + "status": "retired_historical_source", + "assessed_date_utc": "2026-10-03", + "source_base_revision": "56473e4b840f0e6940c031801d866e7e9bf29baf", + "scope": "Retire this historical fixture's supported replay and npm installation entry points; retain offline consistency checks and all original evidence.", + "evidence_class": "offline_artifact_check", + "archived_artifacts": { + "package-original.json.txt": { + "original_path": "package.json", + "bytes": 174, + "sha256": "7bbf63c5eafd347ae5ae56c684be06ef2589d38f2aab580ca7986ca4122bc6a8", + "role": "Original install manifest, resolved by the offline audit for every historical package.json binding." + }, + "run-recording-aid.py.txt": { + "original_path": "run.py", + "bytes": 16427, + "sha256": "be852ce99501f5bc4567b846b90fb0e91d91de77b0eafbd3b72bb4484a2f7d12", + "role": "Future recording aid from the source base; no historical native execution is attributed to these bytes." + } + }, + "retained_lock": { + "path": "package-lock.json", + "bytes": 82463, + "sha256": "5c51ee65cc477f2c1488a38ff5cad1c0a737f81a5b61bbd70d5edc4d15bfc3bb", + "role": "Immutable historical dependency evidence at its original path; still included in scanner inventory." + }, + "current_entrypoints": { + "run.py": { + "sha256": "0df1c6873b8514cb08af2efebe479dfe46c35ad1e6b05d73796bee0d7cca702e", + "role": "Both old source and QMD modes fail with a retirement diagnostic before subprocess or output-directory creation." + }, + "package.json": { + "sha256": "69ed6c37f8517e712ce64e1550c363926bdf25e42d8bfb30efa02b71197f3485", + "role": "Private dependency-free retirement manifest with devEngines.runtime.name=retired-wsl-retrieval and onFail=error." + } + }, + "historical_evidence_policy": "Historical receipts, declared key/check sets, failed attempts, source-review.json, verification.json and prior runner archives are unchanged. The resolver does not add fields to the original receipts.", + "historical_native_acceptance_established": false, + "native_npm_guard_acceptance_established": false, + "npm_guard_scope": "npm 11.19.0 checks devEngines before supported install/ci entry points, including --ignore-scripts; independent native controls are recorded separately by the coordinator. This offline assessment does not establish their execution.", + "guard_limitations": [ + "This is a supported-entry-point guard, not an installation sandbox. Explicit --force, other package managers and restored historical files are outside its claim.", + "Removing package.json alone is insufficient: npm Arborist can load root metadata from a standalone lock.", + "Retirement does not establish a patched dependency, repair missing historical invocation evidence, qualify a current QMD setup or adopt a scanner exception." + ], + "active_qmd_advisory": { + "id": "GHSA-vfj7-8cjw-p6xm", + "status": "unresolved" + }, + "primary_sources_and_pins": [ + { + "repository": "seathatflowsinourveins/native-agent-stack", + "revision": "56473e4b840f0e6940c031801d866e7e9bf29baf", + "url": "https://github.com/seathatflowsinourveins/native-agent-stack/blob/56473e4b840f0e6940c031801d866e7e9bf29baf/blueprints/convergence-practice/wsl-retrieval/audit.py" + }, + { + "repository": "npm/cli", + "revision": "bfacd33ccbcd908480610703b60455d2da5b57a9", + "url": "https://github.com/npm/cli/blob/v11.19.0/docs/lib/content/configuring-npm/package-json.md#L1117" + }, + { + "repository": "npm/cli", + "revision": "bfacd33ccbcd908480610703b60455d2da5b57a9", + "url": "https://github.com/npm/cli/blob/v11.19.0/lib/base-cmd.js#L201" + }, + { + "repository": "npm/cli", + "revision": "bfacd33ccbcd908480610703b60455d2da5b57a9", + "url": "https://github.com/npm/cli/blob/v11.19.0/workspaces/arborist/lib/arborist/load-virtual.js#L50" + }, + { + "url": "https://github.com/advisories/GHSA-vfj7-8cjw-p6xm", + "role": "Primary unresolved advisory; no safe-version claim." + } + ], + "whole_task_provider_usage": null +} diff --git a/blueprints/convergence-practice/wsl-retrieval/run-recording-aid.py.txt b/blueprints/convergence-practice/wsl-retrieval/run-recording-aid.py.txt new file mode 100755 index 000000000..125657bfc --- /dev/null +++ b/blueprints/convergence-practice/wsl-retrieval/run-recording-aid.py.txt @@ -0,0 +1,262 @@ +#!/usr/bin/env python3 +"""Owned Linux retrieval qualification; explicit fixtures and no model commands.""" +import argparse +import ast +import datetime +import hashlib +import json +import os +import platform +import re +import shutil +import sqlite3 +import subprocess +from pathlib import Path +from typing import Any +from urllib.parse import parse_qs, unquote, urlsplit + +HERE = Path(__file__).resolve().parent + + +def sha(path): + return hashlib.sha256(Path(path).read_bytes()).hexdigest() + + +def save(path, value): + Path(path).write_text(json.dumps(value, indent=2, ensure_ascii=False) + '\n') + + +def hashes(path): + return {str(p.relative_to(path)): sha(p) for p in sorted(path.rglob('*')) if p.is_file()} + + +def record_command(label, argv, run, env, path_scopes, facts, result): + """Retain argv boundaries and cwd, replacing private path roots with scope markers.""" + argv = [str(value) for value in argv] + + def sanitize(value): + for path, marker in sorted(path_scopes.items(), key=lambda item: len(str(item[0])), reverse=True): + value = re.sub(re.escape(str(path)) + r'(?=$|/)', lambda _: marker, value) + return value + + fact = {'label': label, 'argv': [sanitize(value) for value in argv], + 'cwd': sanitize(str(run)), 'exit_code': None} + facts.append(fact) + save(run/'receipt.partial.json', result) + stdout, stderr = '', '' + try: + out = subprocess.run(argv, cwd=run, env=env, text=True, + capture_output=True, timeout=30, check=False) + stdout, stderr = out.stdout, out.stderr + fact['exit_code'] = out.returncode + return out, fact + except subprocess.TimeoutExpired as exc: + result['cleanup']['timed_out_commands'] += 1 + fact['failure'] = 'timeout' + stdout, stderr = exc.stdout or '', exc.stderr or '' + raise + except OSError: + fact['failure'] = 'launch_error' + raise + finally: + for suffix, value in [('stdout', stdout), ('stderr', stderr)]: + data = value if isinstance(value, bytes) else value.encode() + (run/(label+'.'+suffix)).write_bytes(data) + fact[suffix+'_bytes'] = len(data) + fact[suffix+'_sha256'] = sha(run/(label+'.'+suffix)) + save(run/'receipt.partial.json', result) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--mode', choices=['source', 'qmd'], required=True) + parser.add_argument('--run-dir', type=Path, required=True) + parser.add_argument('--source', type=Path) + parser.add_argument('--rg', type=Path) + parser.add_argument('--ast-grep', type=Path) + parser.add_argument('--node', type=Path) + parser.add_argument('--package-prefix', type=Path) + args = parser.parse_args() + if platform.system() != 'Linux' or platform.machine() != 'x86_64': + raise ValueError('Linux x86_64 required') + os.umask(0o077) + run = args.run_dir.expanduser().absolute() + repo = next((p for p in HERE.parents if (p/'.git').exists()), HERE) + if run == Path.home() or run.is_relative_to(repo) or any(p.is_symlink() for p in [run, *run.parents]): + raise ValueError('new private run directory outside repository required') + run.mkdir(mode=0o700) + frozen = run/'frozen' + frozen.mkdir() + for name in ['run.py', 'oracle.json', 'pins.json', 'package.json', 'package-lock.json']: + shutil.copyfile(HERE/name, frozen/name) + shutil.copytree(HERE/'seed', frozen/'seed') + frozen_hashes = hashes(frozen) + save(run/'frozen-inputs.json', frozen_hashes) + oracle = json.loads((frozen/'oracle.json').read_text()) + pins = json.loads((frozen/'pins.json').read_text()) + for name in ['tmp', 'cache', 'config']: + (run/name).mkdir() + checks, facts = {}, [] + result: dict[str, Any] = {'schema_version': 1, 'mode': args.mode, 'started_utc': datetime.datetime.now(datetime.timezone.utc).isoformat(), + 'platform': {'system': platform.system(), 'architecture': platform.machine(), 'python': platform.python_version()}, + 'checks': checks, 'facts': facts, 'frozen_inputs': frozen_hashes, + 'global_or_client_changes': False, 'inference_invoked': False, + 'whole_task_provider_usage': None, 'os_confinement_established': False, + 'cleanup': {'persistent_services_started': 0, 'timed_out_commands': 0}} + env = {'HOME': os.environ['HOME'], 'PATH': '/usr/bin:/bin', 'LANG': 'C.UTF-8', + 'TMPDIR': str(run/'tmp'), 'NO_COLOR': '1'} + + def check(name, condition): + if name in checks: + raise ValueError('duplicate check') + checks[name] = bool(condition) + save(run/'receipt.partial.json', result) + if not condition: + raise AssertionError(name) + + path_scopes = {str(run): '', str(Path.home()): ''} + for name in ['source', 'rg', 'ast_grep', 'node', 'package_prefix']: + path = getattr(args, name) + if path is not None: + path_scopes[str(path)] = '<' + name.upper() + '>' + + def command(label, argv, extra_env=None): + return record_command(label, argv, run, env | (extra_env or {}), path_scopes, facts, result) + + def ok(label, argv, extra_env=None): + out, fact = command(label, argv, extra_env) + check(label+'_exit', out.returncode == 0) + return out, fact + + try: + if args.mode == 'source': + if not all([args.source, args.rg, args.ast_grep]): + raise ValueError('source and exact search binaries required') + check('canonical_source_hash', sha(args.source) == oracle['source_sha256']) + fixture = Path('frozen/seed/planner.py') + source = (run/fixture).read_bytes() + check('frozen_source_hash', hashlib.sha256(source).hexdigest() == oracle['source_sha256']) + for key, binary in [('ripgrep', args.rg), ('ast-grep', args.ast_grep)]: + check(key+'_binary_hash', sha(binary) == pins[key]['binary_sha256']) + out, fact = ok(key+'-version', [binary, '--version']) + fact['version_output'] = out.stdout.strip() + check(key+'_version', pins[key]['version'] in out.stdout.splitlines()[0]) + expected_lines = [i for i, line in enumerate(source.decode().splitlines(), 1) if oracle['source_literal'] in line] + check('independent_literal_oracle', expected_lines == oracle['source_expected_line_numbers']) + out, fact = ok('rg-positive', [args.rg, '--no-config', '--json', '-n', '-F', oracle['source_literal'], fixture]) + matches = [x['data'] for line in out.stdout.splitlines() if (x := json.loads(line))['type'] == 'match'] + fact['matches'] = [{'path': x['path']['text'], 'line': x['line_number'], 'line_text': x['lines']['text'], + 'spans': [{'start': s['start'], 'end': s['end'], 'text': s['match']['text']} for s in x['submatches']]} for x in matches] + check('rg_positive_exact_lines', [x['line_number'] for x in matches] == expected_lines) + check('rg_positive_exact_spans', all(x['path']['text'] == str(fixture) and len(x['submatches']) == 1 + and x['lines']['text'].encode()[x['submatches'][0]['start']:x['submatches'][0]['end']] == oracle['source_literal'].encode() for x in matches)) + out, fact = command('rg-negative', [args.rg, '--no-config', '--json', '-n', '-F', oracle['source_negative_marker'], fixture]) + fact['match_count'] = sum(json.loads(line)['type'] == 'match' for line in out.stdout.splitlines()) + check('rg_negative_exit_and_empty', out.returncode == 1 and fact['match_count'] == 0) + expected_ast = sorted([{'start_line': n.lineno, 'start_column': n.col_offset, 'end_line': n.end_lineno, 'end_column': n.end_col_offset} + for n in ast.walk(ast.parse(source)) if isinstance(n, ast.Raise) and isinstance(n.exc, ast.Call) + and isinstance(n.exc.func, ast.Name) and n.exc.func.id == 'ValueError'], key=lambda x: x['start_line'] or 0) + check('independent_ast_oracle', expected_ast == oracle['source_ast_spans']) + out, fact = ok('ast-positive', [args.ast_grep, 'run', '--lang', 'python', '--pattern', oracle['source_ast_pattern'], '--json=compact', fixture]) + matches = json.loads(out.stdout) + fact['matches'] = [{'file': m['file'], 'text': m['text'], 'range': m['range']} for m in matches] + spans = [{'start_line': m['range']['start']['line']+1, 'start_column': m['range']['start']['column'], + 'end_line': m['range']['end']['line']+1, 'end_column': m['range']['end']['column']} for m in matches] + check('ast_exact_spans', spans == expected_ast) + check('ast_bounded_exact_source_text', all(m['file'] == str(fixture) and len(m['text'].encode()) <= 512 + and source[m['range']['byteOffset']['start']:m['range']['byteOffset']['end']] == m['text'].encode() for m in matches)) + out, fact = command('ast-negative', [args.ast_grep, 'run', '--lang', 'python', '--pattern', 'raise AssertionError($$$ARGS)', '--json=compact', fixture]) + fact['matches'] = json.loads(out.stdout) + check('ast_negative_empty', out.returncode in [0, 1] and fact['matches'] == []) + check('canonical_source_unchanged', sha(args.source) == oracle['source_sha256']) + check('source_binaries_unchanged', sha(args.rg) == pins['ripgrep']['binary_sha256'] and sha(args.ast_grep) == pins['ast-grep']['binary_sha256']) + else: + if not args.node or not args.package_prefix: + raise ValueError('exact node and owned package prefix required') + check('node_binary_hash', sha(args.node) == pins['node']['binary_sha256']) + node_out, node_fact = ok('node-version', [args.node, '--version']) + node_fact['version_output'] = node_out.stdout.strip() + check('node_version', node_out.stdout.strip() == 'v'+pins['node']['version']) + package = args.package_prefix.resolve() + qmd = package/'node_modules/@tobilu/qmd' + check('qmd_package_version', json.loads((qmd/'package.json').read_text())['version'] == pins['qmd']['version']) + check('qmd_license_hash', sha(qmd/'LICENSE') == pins['qmd']['license_sha256']) + check('qmd_entrypoint_hash', sha(qmd/'bin/qmd') == pins['qmd']['entrypoint_sha256']) + check('package_lock_hash', sha(package/'package-lock.json') == sha(frozen/'package-lock.json')) + check('no_optional_llama_backends', not (package/'node_modules/@node-llama-cpp').exists() or not any((package/'node_modules/@node-llama-cpp').iterdir())) + shutil.copytree(frozen/'seed/corpus', run/'corpus') + qenv = {'PATH': str(args.node.parent)+':/usr/bin:/bin', 'QMD_CONFIG_DIR': str(run/'config'), + 'XDG_CACHE_HOME': str(run/'cache'), 'QMD_FORCE_CPU': '1'} + argv = [args.node, qmd/'bin/qmd', '--index', oracle['qmd_index']] + out, fact = ok('qmd-version', [args.node, qmd/'bin/qmd', '--version'], qenv) + fact['version_output'] = out.stdout.strip() + check('native_qmd_version', '2.8.3' in out.stdout) + for folder, collection in [('primary', oracle['qmd_collection']), ('decoy', oracle['qmd_decoy_collection'])]: + ok('qmd-add-'+folder, argv+['collection', 'add', run/'corpus'/folder, '--name', collection, '--mask', '**/*.md'], qenv) + + def search(label, query, expected_file, expected_body=None): + out, fact = ok(label, argv+['search', query, '-c', oracle['qmd_collection'], '-n', '3', '--json', '--full'], qenv) + rows = json.loads(out.stdout) + fact['query'] = query + fact['rows'] = rows + uris = [urlsplit(x['file']) for x in rows] + check(label+'_uri_scope', all(u.scheme == 'qmd' and u.netloc == oracle['qmd_collection'] + and parse_qs(u.query) == {'index': [oracle['qmd_index']]} and not u.fragment for u in uris)) + expected_paths = [] if expected_file is None else [expected_file] + check(label+'_paths', [unquote(u.path).removeprefix('/') for u in uris] == expected_paths) + if expected_file is not None: + body = expected_body if expected_body is not None else oracle['corpus']['primary/'+expected_file] + check(label+'_body', rows[0]['body'] == body) + check(label+'_bound', len(out.stdout.encode()) <= 2048 and len(rows) <= 3) + return rows + + for i, item in enumerate(oracle['queries']): + search('qmd-positive-'+str(i), item['query'], item['file']) + for i, query in enumerate(oracle['negative_queries']): + search('qmd-negative-'+str(i), query, None) + target = 'qmd://'+oracle['qmd_collection']+'/recovery.md' + out, fact = ok('qmd-bounded-get', argv+['get', target+':2:1', '--no-line-numbers'], qenv) + fact['output'] = out.stdout + line = oracle['corpus']['primary/recovery.md'].splitlines()[1] + check('qmd_get_exact_bounded_line', target in out.stdout and line in out.stdout.splitlines() and len(out.stdout.encode()) <= 1024 + and oracle['corpus']['primary/recovery.md'].splitlines()[2] not in out.stdout) + search('qmd-reopen', 'Cobaltcheckpoint', 'recovery.md') + (run/'corpus/primary/recovery.md').write_text(oracle['updated_recovery']) + (run/'corpus/primary'/oracle['deleted_path']).unlink() + ok('qmd-update', argv+['update'], qenv) + search('qmd-updated', oracle['after_update_marker'], 'recovery.md', oracle['updated_recovery']) + search('qmd-old-marker', 'Cobaltcheckpoint', None) + search('qmd-deleted', 'Amberproject', None) + dbs = list((run/'cache').rglob('*.sqlite')) + check('single_owned_database', len(dbs) == 1) + with sqlite3.connect(dbs[0].as_uri()+'?mode=ro', uri=True) as db: + tables = {r[0] for r in db.execute("SELECT name FROM sqlite_master WHERE type='table'")} + vector_rows = db.execute('SELECT count(*) FROM content_vectors').fetchone()[0] if 'content_vectors' in tables else 0 + result['database'] = {'integrity': db.execute('PRAGMA integrity_check').fetchone()[0], + 'active_documents': db.execute('SELECT count(*) FROM documents WHERE active=1').fetchone()[0], + 'content_vectors_table': 'content_vectors' in tables, 'content_vector_rows': vector_rows} + check('qmd_database_integrity', result['database']['integrity'] == 'ok') + check('qmd_active_scope_count', result['database']['active_documents'] == 3) + check('qmd_no_embedding_rows', vector_rows == 0) + model_files = [str(p.relative_to(run)) for p in run.rglob('*') if p.is_file() and p.suffix in oracle['no_model_files_suffixes']] + result['model_files_in_run'] = model_files + check('qmd_no_model_files', model_files == []) + check('qmd_entrypoint_and_lock_unchanged', sha(qmd/'bin/qmd') == pins['qmd']['entrypoint_sha256'] and sha(package/'package-lock.json') == sha(frozen/'package-lock.json')) + check('frozen_inputs_unchanged', hashes(frozen) == frozen_hashes) + check('owned_commands_completed', result['cleanup']['timed_out_commands'] == 0) + result['passed'] = True + except Exception as exc: # noqa: BLE001 - retain the one attempted native run and its raw logs + result['passed'] = False + result['failure'] = {'type': type(exc).__name__, 'message': str(exc)} + finally: + result['finished_utc'] = datetime.datetime.now(datetime.timezone.utc).isoformat() + result['native_commands'] = len(facts) + save(run/'receipt.json', result) + print(json.dumps({'mode': args.mode, 'passed': result['passed'], 'checks': len(checks), + 'failed_checks': [k for k, value in checks.items() if not value], + 'native_commands': len(facts), 'failure': result.get('failure')})) + return 0 if result['passed'] else 1 + + +if __name__ == '__main__': + raise SystemExit(main()) diff --git a/blueprints/convergence-practice/wsl-retrieval/run.py b/blueprints/convergence-practice/wsl-retrieval/run.py old mode 100755 new mode 100644 index 125657bfc..690cfd706 --- a/blueprints/convergence-practice/wsl-retrieval/run.py +++ b/blueprints/convergence-practice/wsl-retrieval/run.py @@ -1,262 +1,19 @@ #!/usr/bin/env python3 -"""Owned Linux retrieval qualification; explicit fixtures and no model commands.""" +"""Retired historical WSL retrieval fixture; offline audit only.""" import argparse -import ast -import datetime -import hashlib -import json -import os -import platform -import re -import shutil -import sqlite3 -import subprocess -from pathlib import Path -from typing import Any -from urllib.parse import parse_qs, unquote, urlsplit - -HERE = Path(__file__).resolve().parent - - -def sha(path): - return hashlib.sha256(Path(path).read_bytes()).hexdigest() - - -def save(path, value): - Path(path).write_text(json.dumps(value, indent=2, ensure_ascii=False) + '\n') - - -def hashes(path): - return {str(p.relative_to(path)): sha(p) for p in sorted(path.rglob('*')) if p.is_file()} - - -def record_command(label, argv, run, env, path_scopes, facts, result): - """Retain argv boundaries and cwd, replacing private path roots with scope markers.""" - argv = [str(value) for value in argv] - - def sanitize(value): - for path, marker in sorted(path_scopes.items(), key=lambda item: len(str(item[0])), reverse=True): - value = re.sub(re.escape(str(path)) + r'(?=$|/)', lambda _: marker, value) - return value - - fact = {'label': label, 'argv': [sanitize(value) for value in argv], - 'cwd': sanitize(str(run)), 'exit_code': None} - facts.append(fact) - save(run/'receipt.partial.json', result) - stdout, stderr = '', '' - try: - out = subprocess.run(argv, cwd=run, env=env, text=True, - capture_output=True, timeout=30, check=False) - stdout, stderr = out.stdout, out.stderr - fact['exit_code'] = out.returncode - return out, fact - except subprocess.TimeoutExpired as exc: - result['cleanup']['timed_out_commands'] += 1 - fact['failure'] = 'timeout' - stdout, stderr = exc.stdout or '', exc.stderr or '' - raise - except OSError: - fact['failure'] = 'launch_error' - raise - finally: - for suffix, value in [('stdout', stdout), ('stderr', stderr)]: - data = value if isinstance(value, bytes) else value.encode() - (run/(label+'.'+suffix)).write_bytes(data) - fact[suffix+'_bytes'] = len(data) - fact[suffix+'_sha256'] = sha(run/(label+'.'+suffix)) - save(run/'receipt.partial.json', result) def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--mode', choices=['source', 'qmd'], required=True) - parser.add_argument('--run-dir', type=Path, required=True) - parser.add_argument('--source', type=Path) - parser.add_argument('--rg', type=Path) - parser.add_argument('--ast-grep', type=Path) - parser.add_argument('--node', type=Path) - parser.add_argument('--package-prefix', type=Path) - args = parser.parse_args() - if platform.system() != 'Linux' or platform.machine() != 'x86_64': - raise ValueError('Linux x86_64 required') - os.umask(0o077) - run = args.run_dir.expanduser().absolute() - repo = next((p for p in HERE.parents if (p/'.git').exists()), HERE) - if run == Path.home() or run.is_relative_to(repo) or any(p.is_symlink() for p in [run, *run.parents]): - raise ValueError('new private run directory outside repository required') - run.mkdir(mode=0o700) - frozen = run/'frozen' - frozen.mkdir() - for name in ['run.py', 'oracle.json', 'pins.json', 'package.json', 'package-lock.json']: - shutil.copyfile(HERE/name, frozen/name) - shutil.copytree(HERE/'seed', frozen/'seed') - frozen_hashes = hashes(frozen) - save(run/'frozen-inputs.json', frozen_hashes) - oracle = json.loads((frozen/'oracle.json').read_text()) - pins = json.loads((frozen/'pins.json').read_text()) - for name in ['tmp', 'cache', 'config']: - (run/name).mkdir() - checks, facts = {}, [] - result: dict[str, Any] = {'schema_version': 1, 'mode': args.mode, 'started_utc': datetime.datetime.now(datetime.timezone.utc).isoformat(), - 'platform': {'system': platform.system(), 'architecture': platform.machine(), 'python': platform.python_version()}, - 'checks': checks, 'facts': facts, 'frozen_inputs': frozen_hashes, - 'global_or_client_changes': False, 'inference_invoked': False, - 'whole_task_provider_usage': None, 'os_confinement_established': False, - 'cleanup': {'persistent_services_started': 0, 'timed_out_commands': 0}} - env = {'HOME': os.environ['HOME'], 'PATH': '/usr/bin:/bin', 'LANG': 'C.UTF-8', - 'TMPDIR': str(run/'tmp'), 'NO_COLOR': '1'} - - def check(name, condition): - if name in checks: - raise ValueError('duplicate check') - checks[name] = bool(condition) - save(run/'receipt.partial.json', result) - if not condition: - raise AssertionError(name) - - path_scopes = {str(run): '', str(Path.home()): ''} - for name in ['source', 'rg', 'ast_grep', 'node', 'package_prefix']: - path = getattr(args, name) - if path is not None: - path_scopes[str(path)] = '<' + name.upper() + '>' - - def command(label, argv, extra_env=None): - return record_command(label, argv, run, env | (extra_env or {}), path_scopes, facts, result) - - def ok(label, argv, extra_env=None): - out, fact = command(label, argv, extra_env) - check(label+'_exit', out.returncode == 0) - return out, fact - - try: - if args.mode == 'source': - if not all([args.source, args.rg, args.ast_grep]): - raise ValueError('source and exact search binaries required') - check('canonical_source_hash', sha(args.source) == oracle['source_sha256']) - fixture = Path('frozen/seed/planner.py') - source = (run/fixture).read_bytes() - check('frozen_source_hash', hashlib.sha256(source).hexdigest() == oracle['source_sha256']) - for key, binary in [('ripgrep', args.rg), ('ast-grep', args.ast_grep)]: - check(key+'_binary_hash', sha(binary) == pins[key]['binary_sha256']) - out, fact = ok(key+'-version', [binary, '--version']) - fact['version_output'] = out.stdout.strip() - check(key+'_version', pins[key]['version'] in out.stdout.splitlines()[0]) - expected_lines = [i for i, line in enumerate(source.decode().splitlines(), 1) if oracle['source_literal'] in line] - check('independent_literal_oracle', expected_lines == oracle['source_expected_line_numbers']) - out, fact = ok('rg-positive', [args.rg, '--no-config', '--json', '-n', '-F', oracle['source_literal'], fixture]) - matches = [x['data'] for line in out.stdout.splitlines() if (x := json.loads(line))['type'] == 'match'] - fact['matches'] = [{'path': x['path']['text'], 'line': x['line_number'], 'line_text': x['lines']['text'], - 'spans': [{'start': s['start'], 'end': s['end'], 'text': s['match']['text']} for s in x['submatches']]} for x in matches] - check('rg_positive_exact_lines', [x['line_number'] for x in matches] == expected_lines) - check('rg_positive_exact_spans', all(x['path']['text'] == str(fixture) and len(x['submatches']) == 1 - and x['lines']['text'].encode()[x['submatches'][0]['start']:x['submatches'][0]['end']] == oracle['source_literal'].encode() for x in matches)) - out, fact = command('rg-negative', [args.rg, '--no-config', '--json', '-n', '-F', oracle['source_negative_marker'], fixture]) - fact['match_count'] = sum(json.loads(line)['type'] == 'match' for line in out.stdout.splitlines()) - check('rg_negative_exit_and_empty', out.returncode == 1 and fact['match_count'] == 0) - expected_ast = sorted([{'start_line': n.lineno, 'start_column': n.col_offset, 'end_line': n.end_lineno, 'end_column': n.end_col_offset} - for n in ast.walk(ast.parse(source)) if isinstance(n, ast.Raise) and isinstance(n.exc, ast.Call) - and isinstance(n.exc.func, ast.Name) and n.exc.func.id == 'ValueError'], key=lambda x: x['start_line'] or 0) - check('independent_ast_oracle', expected_ast == oracle['source_ast_spans']) - out, fact = ok('ast-positive', [args.ast_grep, 'run', '--lang', 'python', '--pattern', oracle['source_ast_pattern'], '--json=compact', fixture]) - matches = json.loads(out.stdout) - fact['matches'] = [{'file': m['file'], 'text': m['text'], 'range': m['range']} for m in matches] - spans = [{'start_line': m['range']['start']['line']+1, 'start_column': m['range']['start']['column'], - 'end_line': m['range']['end']['line']+1, 'end_column': m['range']['end']['column']} for m in matches] - check('ast_exact_spans', spans == expected_ast) - check('ast_bounded_exact_source_text', all(m['file'] == str(fixture) and len(m['text'].encode()) <= 512 - and source[m['range']['byteOffset']['start']:m['range']['byteOffset']['end']] == m['text'].encode() for m in matches)) - out, fact = command('ast-negative', [args.ast_grep, 'run', '--lang', 'python', '--pattern', 'raise AssertionError($$$ARGS)', '--json=compact', fixture]) - fact['matches'] = json.loads(out.stdout) - check('ast_negative_empty', out.returncode in [0, 1] and fact['matches'] == []) - check('canonical_source_unchanged', sha(args.source) == oracle['source_sha256']) - check('source_binaries_unchanged', sha(args.rg) == pins['ripgrep']['binary_sha256'] and sha(args.ast_grep) == pins['ast-grep']['binary_sha256']) - else: - if not args.node or not args.package_prefix: - raise ValueError('exact node and owned package prefix required') - check('node_binary_hash', sha(args.node) == pins['node']['binary_sha256']) - node_out, node_fact = ok('node-version', [args.node, '--version']) - node_fact['version_output'] = node_out.stdout.strip() - check('node_version', node_out.stdout.strip() == 'v'+pins['node']['version']) - package = args.package_prefix.resolve() - qmd = package/'node_modules/@tobilu/qmd' - check('qmd_package_version', json.loads((qmd/'package.json').read_text())['version'] == pins['qmd']['version']) - check('qmd_license_hash', sha(qmd/'LICENSE') == pins['qmd']['license_sha256']) - check('qmd_entrypoint_hash', sha(qmd/'bin/qmd') == pins['qmd']['entrypoint_sha256']) - check('package_lock_hash', sha(package/'package-lock.json') == sha(frozen/'package-lock.json')) - check('no_optional_llama_backends', not (package/'node_modules/@node-llama-cpp').exists() or not any((package/'node_modules/@node-llama-cpp').iterdir())) - shutil.copytree(frozen/'seed/corpus', run/'corpus') - qenv = {'PATH': str(args.node.parent)+':/usr/bin:/bin', 'QMD_CONFIG_DIR': str(run/'config'), - 'XDG_CACHE_HOME': str(run/'cache'), 'QMD_FORCE_CPU': '1'} - argv = [args.node, qmd/'bin/qmd', '--index', oracle['qmd_index']] - out, fact = ok('qmd-version', [args.node, qmd/'bin/qmd', '--version'], qenv) - fact['version_output'] = out.stdout.strip() - check('native_qmd_version', '2.8.3' in out.stdout) - for folder, collection in [('primary', oracle['qmd_collection']), ('decoy', oracle['qmd_decoy_collection'])]: - ok('qmd-add-'+folder, argv+['collection', 'add', run/'corpus'/folder, '--name', collection, '--mask', '**/*.md'], qenv) - - def search(label, query, expected_file, expected_body=None): - out, fact = ok(label, argv+['search', query, '-c', oracle['qmd_collection'], '-n', '3', '--json', '--full'], qenv) - rows = json.loads(out.stdout) - fact['query'] = query - fact['rows'] = rows - uris = [urlsplit(x['file']) for x in rows] - check(label+'_uri_scope', all(u.scheme == 'qmd' and u.netloc == oracle['qmd_collection'] - and parse_qs(u.query) == {'index': [oracle['qmd_index']]} and not u.fragment for u in uris)) - expected_paths = [] if expected_file is None else [expected_file] - check(label+'_paths', [unquote(u.path).removeprefix('/') for u in uris] == expected_paths) - if expected_file is not None: - body = expected_body if expected_body is not None else oracle['corpus']['primary/'+expected_file] - check(label+'_body', rows[0]['body'] == body) - check(label+'_bound', len(out.stdout.encode()) <= 2048 and len(rows) <= 3) - return rows - - for i, item in enumerate(oracle['queries']): - search('qmd-positive-'+str(i), item['query'], item['file']) - for i, query in enumerate(oracle['negative_queries']): - search('qmd-negative-'+str(i), query, None) - target = 'qmd://'+oracle['qmd_collection']+'/recovery.md' - out, fact = ok('qmd-bounded-get', argv+['get', target+':2:1', '--no-line-numbers'], qenv) - fact['output'] = out.stdout - line = oracle['corpus']['primary/recovery.md'].splitlines()[1] - check('qmd_get_exact_bounded_line', target in out.stdout and line in out.stdout.splitlines() and len(out.stdout.encode()) <= 1024 - and oracle['corpus']['primary/recovery.md'].splitlines()[2] not in out.stdout) - search('qmd-reopen', 'Cobaltcheckpoint', 'recovery.md') - (run/'corpus/primary/recovery.md').write_text(oracle['updated_recovery']) - (run/'corpus/primary'/oracle['deleted_path']).unlink() - ok('qmd-update', argv+['update'], qenv) - search('qmd-updated', oracle['after_update_marker'], 'recovery.md', oracle['updated_recovery']) - search('qmd-old-marker', 'Cobaltcheckpoint', None) - search('qmd-deleted', 'Amberproject', None) - dbs = list((run/'cache').rglob('*.sqlite')) - check('single_owned_database', len(dbs) == 1) - with sqlite3.connect(dbs[0].as_uri()+'?mode=ro', uri=True) as db: - tables = {r[0] for r in db.execute("SELECT name FROM sqlite_master WHERE type='table'")} - vector_rows = db.execute('SELECT count(*) FROM content_vectors').fetchone()[0] if 'content_vectors' in tables else 0 - result['database'] = {'integrity': db.execute('PRAGMA integrity_check').fetchone()[0], - 'active_documents': db.execute('SELECT count(*) FROM documents WHERE active=1').fetchone()[0], - 'content_vectors_table': 'content_vectors' in tables, 'content_vector_rows': vector_rows} - check('qmd_database_integrity', result['database']['integrity'] == 'ok') - check('qmd_active_scope_count', result['database']['active_documents'] == 3) - check('qmd_no_embedding_rows', vector_rows == 0) - model_files = [str(p.relative_to(run)) for p in run.rglob('*') if p.is_file() and p.suffix in oracle['no_model_files_suffixes']] - result['model_files_in_run'] = model_files - check('qmd_no_model_files', model_files == []) - check('qmd_entrypoint_and_lock_unchanged', sha(qmd/'bin/qmd') == pins['qmd']['entrypoint_sha256'] and sha(package/'package-lock.json') == sha(frozen/'package-lock.json')) - check('frozen_inputs_unchanged', hashes(frozen) == frozen_hashes) - check('owned_commands_completed', result['cleanup']['timed_out_commands'] == 0) - result['passed'] = True - except Exception as exc: # noqa: BLE001 - retain the one attempted native run and its raw logs - result['passed'] = False - result['failure'] = {'type': type(exc).__name__, 'message': str(exc)} - finally: - result['finished_utc'] = datetime.datetime.now(datetime.timezone.utc).isoformat() - result['native_commands'] = len(facts) - save(run/'receipt.json', result) - print(json.dumps({'mode': args.mode, 'passed': result['passed'], 'checks': len(checks), - 'failed_checks': [k for k, value in checks.items() if not value], - 'native_commands': len(facts), 'failure': result.get('failure')})) - return 0 if result['passed'] else 1 + parser.add_argument('--run-dir') + for name in ['source', 'rg', 'ast-grep', 'node', 'package-prefix']: + parser.add_argument('--'+name) + parser.parse_args() + parser.exit(1, 'WSL retrieval fixture retired: source/QMD replay is disabled. ' + 'Run audit.py for offline historical consistency checks; ' + 'see README.md for the separate current QMD recipe and unresolved advisory.\n') if __name__ == '__main__': - raise SystemExit(main()) + main() diff --git a/docs/decisions/2026-09-22-github-automation-closure.md b/docs/decisions/2026-09-22-github-automation-closure.md index fb2a81f03..bd7a2ef0d 100644 --- a/docs/decisions/2026-09-22-github-automation-closure.md +++ b/docs/decisions/2026-09-22-github-automation-closure.md @@ -340,7 +340,7 @@ locally with `GH_TOKEN` set and no `--offline`, using together. The carried-forward oauthlib review holds: the four oauthlib source files it names are byte-identical in the new venv and oauthlib 3.3.1 is unchanged. The macOS lock is a frozen evidence artifact for which the tree records no owner (its only commit is #201, and neither its experiment record nor `docs/lanes.md` names one), so it is not patched: its advisory is - excepted until 2026-12-24 under the policy above. **The exception is scoped by the scanner, not by a reader of the lock.** + excepted until 2026-12-24 under the policy above. **The workflow scopes the exception to its reviewed input.** OSV-Scanner 2.6.0 applies an explicit `--config` to every input of one invocation (`docs/configuration.md`, `internal/config/manager.go`, `Manager.Get`), so an `[[IgnoredVulns]]` entry in the one config would hide the advisory in every lock of the inventory. The exception therefore lives in `.github/osv-scanner-frozen-macos.toml` (one entry, nothing @@ -366,6 +366,43 @@ locally with `GH_TOKEN` set and no `--offline`, using for urllib3 2.8.0 or PyJWT 2.15.0 that only a later release fixes. **Not covered:** the image's server binary (PyInstaller build of upstream's unchanged uv.lock, urllib3 2.7.0 and PyJWT 2.13.0 or older) and the grader venv, as for the earlier PyJWT relock. Evidence: `evidence/receipts/osv-urllib3-next-20260930.json`. + **Retired historical WSL retrieval partition (2026-10-03).** The original + `blueprints/convergence-practice/wsl-retrieval/package-lock.json` remains at + SHA256 `5c51ee65cc477f2c1488a38ff5cad1c0a737f81a5b61bbd70d5edc4d15bfc3bb`. + Its braces 3.0.3 advisory, GHSA-vfj7-8cjw-p6xm, is assigned only to + `.github/osv-scanner-frozen-wsl-retrieval.toml`, expiring at the bare TOML + date 2026-10-17. The dated reason names the supported-entry-point retirement, + its limitations, the exact lock and digest, the retirement assessment and + `evidence/receipts/wsl-retrieval-retirement-20261003.json`. Retirement preserves + vulnerable historical bytes; it does not qualify active QMD. + + The workflow now scans three exhaustive, disjoint inventory groups under + ordinary, frozen macOS and retired WSL configs. Before invoking OSV, the + native unittest preflight rejects unlisted or duplicated inputs, unknown + configs, advisory or package-override leakage, digest changes, missing or + mismatched retirement evidence, a restored assessment status, unbound reasons + and expired grants. The shell also checks the two exact archive assignments. + Each group's primary and SARIF statuses are retained, and the largest observed + status becomes the step status. Off pull requests, all three reports are kept + and uploaded under separate categories by the existing tool-free write job. + + **Sources and correction.** Reuse the reviewed macOS partition at repository + revision `56473e4b840f0e6940c031801d866e7e9bf29baf`, and OSV-Scanner v2.6.0 + (`e840a6e8adb14b7777c78e26cfbf6e2abc1d1fc6`): installed version/help, the + [tagged release](https://github.com/google/osv-scanner/releases/tag/v2.6.0), + [configuration reference](https://github.com/google/osv-scanner/blob/v2.6.0/docs/configuration.md), + [Manager.Get](https://github.com/google/osv-scanner/blob/v2.6.0/internal/config/manager.go) + and [ShouldIgnore](https://github.com/google/osv-scanner/blob/v2.6.0/internal/config/config.go). + The older prose attributed path scope to the scanner. `Manager.Get` returns + the explicit override for every target path; caller partitioning enforces + scope. Mutation tests exercise the actual policy guards, and recording doubles + execute the workflow shell to verify routing, error propagation and SARIF + retention. These are local integration and synthetic checks; native scanner + controls and final integrated CI remain separate acceptance evidence. + **Overturn:** supported replay/install entry points return, the lock changes, + the source/evidence binding fails, or the date reaches 2026-10-17. Reassess the + disposition and remove or replace the dedicated grant; do not extend it to + active inputs or relock historical evidence without its owner. - **Triggers and permissions.** `pull_request` (no path filter), push to `main`, Wednesday `37 5 * * 3`, and dispatch. The PR run is the required diff --git a/docs/ecosystem/manifest.json b/docs/ecosystem/manifest.json index a3e6e3c42..3ec6fd0d5 100644 --- a/docs/ecosystem/manifest.json +++ b/docs/ecosystem/manifest.json @@ -725,9 +725,9 @@ }, { "id": "wsl-retrieval-20260920", - "title": "Incomplete historical WSL retrieval reference", - "status": "Native acceptance incomplete", - "body": "Retained source and QMD fixture receipts lack command argv/cwd; raw logs could not be recovered. Historical npm --ignore-scripts is not the supported fresh-install recipe. Selected facts and the failed attempt remain inspectable, but do not qualify native E2E or adoption. Future runs must retain invocation provenance and use supported QMD installation controls.", + "title": "Retired historical WSL retrieval reference", + "status": "Retired; historical native acceptance incomplete", + "body": "Supported replay and npm installation entry points are retired; original receipts, archives and lock bytes remain available for offline inspection. Historical command argv/cwd and raw logs remain missing. Eight native npm guard controls and isolated scanner ignore/expiry controls are recorded separately in wsl-retrieval-retirement-20261003; they do not qualify active QMD, repair the braces advisory or establish new-host acceptance.", "path": "blueprints/convergence-practice/wsl-retrieval/README.md" } ], diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/candidate-archive-expired-invalid-attempt1.toml b/evidence/artifacts/wsl-retrieval-retirement-20261003/candidate-archive-expired-invalid-attempt1.toml new file mode 100644 index 000000000..a5e390b51 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/candidate-archive-expired-invalid-attempt1.toml @@ -0,0 +1,5 @@ +# Identical native scope control with an already past date; system time unchanged. +[[IgnoredVulns]] +id = "GHSA-vfj7-8cjw-p6xm" +ignoreUntil = "2026-10-01" +reason = "Prospective control for the hash-bound retired historical WSL retrieval lock only (SHA2565c51ee65cc477f2c1488a38ff5cad1c0a737f81a5b61bbd70d5edc4d15bfc3bb), pending actual source-retirement and native-control acceptance; not an active QMD exception or vulnerability fix. Root scoped handoff608#5963764481 and Astra retirement design acceptance require exact inventory, digest, historical preservation, expiry and ordinary active-input controls before adoption. braces3.0.3 remains affected by GHSA-vfj7-8cjw-p6xm/CVE-2026-93687; no patched release in reviewed primary evidence. This scratch config tests native ignore/expiry behavior only." diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/candidate-archive-expired.toml b/evidence/artifacts/wsl-retrieval-retirement-20261003/candidate-archive-expired.toml new file mode 100644 index 000000000..0c1848bb8 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/candidate-archive-expired.toml @@ -0,0 +1,5 @@ +# Identical native scope control with an already past date; system time unchanged. +[[IgnoredVulns]] +id = "GHSA-vfj7-8cjw-p6xm" +ignoreUntil = 2026-10-01 +reason = "Prospective control for the hash-bound retired historical WSL retrieval lock only (SHA2565c51ee65cc477f2c1488a38ff5cad1c0a737f81a5b61bbd70d5edc4d15bfc3bb), pending actual source-retirement and native-control acceptance; not an active QMD exception or vulnerability fix. Root scoped handoff608#5963764481 and Astra retirement design acceptance require exact inventory, digest, historical preservation, expiry and ordinary active-input controls before adoption. braces3.0.3 remains affected by GHSA-vfj7-8cjw-p6xm/CVE-2026-93687; no patched release in reviewed primary evidence. This scratch config tests native ignore/expiry behavior only." diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/candidate-archive-invalid-attempt1.toml b/evidence/artifacts/wsl-retrieval-retirement-20261003/candidate-archive-invalid-attempt1.toml new file mode 100644 index 000000000..a8a7e678f --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/candidate-archive-invalid-attempt1.toml @@ -0,0 +1,7 @@ +# Prospective isolated control only; no repository or active-input assignment. +# OSV-Scanner v2.6.0 config applies to every input of an invocation. Caller scope +# must therefore be separately enforced by exact inventory/hash controls. +[[IgnoredVulns]] +id = "GHSA-vfj7-8cjw-p6xm" +ignoreUntil = "2026-10-31" +reason = "Prospective control for the hash-bound retired historical WSL retrieval lock only (SHA2565c51ee65cc477f2c1488a38ff5cad1c0a737f81a5b61bbd70d5edc4d15bfc3bb), pending actual source-retirement and native-control acceptance; not an active QMD exception or vulnerability fix. Root scoped handoff608#5963764481 and Astra retirement design acceptance require exact inventory, digest, historical preservation, expiry and ordinary active-input controls before adoption. braces3.0.3 remains affected by GHSA-vfj7-8cjw-p6xm/CVE-2026-93687; no patched release in reviewed primary evidence. This scratch config tests native ignore/expiry behavior only." diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/candidate-archive.toml b/evidence/artifacts/wsl-retrieval-retirement-20261003/candidate-archive.toml new file mode 100644 index 000000000..26d6b3e95 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/candidate-archive.toml @@ -0,0 +1,7 @@ +# Prospective isolated control only; no repository or active-input assignment. +# OSV-Scanner v2.6.0 config applies to every input of an invocation. Caller scope +# must therefore be separately enforced by exact inventory/hash controls. +[[IgnoredVulns]] +id = "GHSA-vfj7-8cjw-p6xm" +ignoreUntil = 2026-10-31 +reason = "Prospective control for the hash-bound retired historical WSL retrieval lock only (SHA2565c51ee65cc477f2c1488a38ff5cad1c0a737f81a5b61bbd70d5edc4d15bfc3bb), pending actual source-retirement and native-control acceptance; not an active QMD exception or vulnerability fix. Root scoped handoff608#5963764481 and Astra retirement design acceptance require exact inventory, digest, historical preservation, expiry and ordinary active-input controls before adoption. braces3.0.3 remains affected by GHSA-vfj7-8cjw-p6xm/CVE-2026-93687; no patched release in reviewed primary evidence. This scratch config tests native ignore/expiry behavior only." diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/ci-correction.md b/evidence/artifacts/wsl-retrieval-retirement-20261003/ci-correction.md new file mode 100644 index 000000000..244c29bbf --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/ci-correction.md @@ -0,0 +1,9 @@ +# Hosted convergence-discovery correction + +Head `0218ad8970eddf213100abed3fe0b004482f1cc9` passed hosted OSV, but both validation jobs failed the all-recorded convergence discovery command. Native logs: [Linux job](https://github.com/seathatflowsinourveins/native-agent-stack/actions/runs/37090864261/job/111110727384), [macOS job](https://github.com/seathatflowsinourveins/native-agent-stack/actions/runs/37090864271/job/111110726967). Both returned 1 at `python3 scripts/validate_convergence.py --all-recorded --root . --json`, with `record discovery: invalid, changed or empty evidence manifest`. Local reproduction returned the same output and exit 1. Earlier scoped convergence and registry validation did not cover discovery of every JSON output. + +The repository's [discovery implementation at that head](https://github.com/seathatflowsinourveins/native-agent-stack/blob/0218ad8970eddf213100abed3fe0b004482f1cc9/scripts/validate_convergence.py#L203) parses every registered JSON artifact. Two retained first-attempt scanner failures produced genuinely empty stdout. Publishing those zero-byte outputs with `.json` suffixes made discovery fail. Their public suffixes are now `.stdout.txt`; controls and registry references are updated. The bytes remain empty (SHA256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855). Original private captures and failed stderr/config artifacts remain preserved. No scanner, lock or validation rule changes. + +A separate macOS harden-runner installation warning was nonfatal and did not cause the job's exit. Log retrieval first returned 1 because gh rejected terminal escape sequences; retry with the installed `--allow-escape-sequences` option captured logs privately and returned 0. Public links above remain the independent hosted observation. + +The first local correction made all-recorded discovery pass (26 records), but publication validation returned 1: the new correction note lacked its required byte count, and the index still listed the two deleted JSON paths before the rename was staged. The coordinator mistakenly committed/pushed `cf217a88ab087d1ad891f2493e9025dab29a6bf6` before inspecting that failed validation result. This was not a passing publication claim. The follow-up uses the repository's supported `host_receipts.register_file` operation for the correction note, stages the rename before validation, and requires a verified zero exit before committing. The earlier failed head and outputs remain retained. diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-active-control.exit.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-active-control.exit.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-active-control.exit.txt @@ -0,0 +1 @@ +1 diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-active-control.stderr.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-active-control.stderr.txt new file mode 100644 index 000000000..2ba9affe6 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-active-control.stderr.txt @@ -0,0 +1,8 @@ +Starting filesystem walk for root: / +Scanned ${ACTIVE_CONTROL}/package-lock.json file and found 170 packages +End status: 0 dirs visited, 1 inodes visited, 1 Extract calls, 5.337764ms elapsed, 5.337844ms wall time +.github/osv-scanner.toml has unused ignores: + - GHSA-8mgp-746c-j5xp + - GHSA-h35f-9h28-mq5c + - GHSA-hj66-6f7g-4r5v + - GHSA-xpv3-w29h-x7cv diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-active-control.stdout.json b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-active-control.stdout.json new file mode 100644 index 000000000..406459136 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-active-control.stdout.json @@ -0,0 +1,124 @@ +{ + "results": [ + { + "source": { + "path": "${ACTIVE_CONTROL}/package-lock.json", + "type": "lockfile" + }, + "packages": [ + { + "package": { + "name": "braces", + "version": "3.0.3", + "ecosystem": "npm" + }, + "groups": [ + { + "ids": [ + "GHSA-vfj7-8cjw-p6xm" + ], + "aliases": [ + "CVE-2026-93687", + "GHSA-vfj7-8cjw-p6xm" + ], + "max_severity": "8.7" + } + ], + "vulnerabilities": [ + { + "affected": [ + { + "database_specific": { + "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/09/GHSA-vfj7-8cjw-p6xm/GHSA-vfj7-8cjw-p6xm.json" + }, + "package": { + "ecosystem": "npm", + "name": "braces", + "purl": "pkg:npm/braces" + }, + "ranges": [ + { + "events": [ + { + "introduced": "0" + }, + { + "last_affected": "3.0.3" + } + ], + "type": "SEMVER" + } + ] + } + ], + "aliases": [ + "CVE-2026-93687" + ], + "database_specific": { + "cwe_ids": [ + "CWE-674" + ], + "github_reviewed": true, + "github_reviewed_at": "2026-10-02T22:36:33Z", + "nvd_published_at": "2026-09-18T16:17:15Z", + "severity": "HIGH" + }, + "details": "braces through 3.0.3 contains a stack overflow vulnerability in the recursive AST walkers that lack depth guards. Attackers can supply deeply nested brace patterns under the character limit to exhaust the call stack and terminate the Node.js process with an uncaught RangeError.", + "id": "GHSA-vfj7-8cjw-p6xm", + "modified": "2026-10-02T22:45:04.328737432Z", + "published": "2026-09-18T18:31:41Z", + "references": [ + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-93687" + }, + { + "type": "WEB", + "url": "https://github.com/micromatch/braces/issues/70" + }, + { + "type": "PACKAGE", + "url": "https://github.com/micromatch/braces" + }, + { + "type": "WEB", + "url": "https://github.com/micromatch/braces/blob/3.0.3/lib/compile.js#L49-L53" + }, + { + "type": "WEB", + "url": "https://github.com/micromatch/braces/blob/3.0.3/lib/expand.js#L102-L105" + }, + { + "type": "WEB", + "url": "https://github.com/micromatch/braces/blob/3.0.3/lib/parse.js#L38-L40" + }, + { + "type": "WEB", + "url": "https://www.vulncheck.com/advisories/braces-through-3.0.3-stack-overflow-via-deeply-nested-patterns" + } + ], + "schema_version": "1.9.0", + "severity": [ + { + "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H", + "type": "CVSS_V3" + }, + { + "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N", + "type": "CVSS_V4" + } + ], + "summary": "braces vulnerable to stack-exhaustion denial of service through deeply nested patterns" + } + ] + } + ] + } + ], + "experimental_config": { + "licenses": { + "summary": false, + "allowlist": null + } + } +} diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-expired-final-config.toml b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-expired-final-config.toml new file mode 100644 index 000000000..37380d6a6 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-expired-final-config.toml @@ -0,0 +1,12 @@ +# Dedicated OSV-Scanner v2.6.0 configuration for the retired historical WSL retrieval lock only. +# The explicit --config applies to every input of its invocation, not to a path encoded in this file: +# https://github.com/google/osv-scanner/blob/v2.6.0/docs/configuration.md +# https://github.com/google/osv-scanner/blob/v2.6.0/internal/config/manager.go (Manager.Get). +# The workflow's three disjoint input lists and preflight policy tests enforce the exact caller scope, +# reviewed digest, evidence, retirement guard and expiry. No ordinary or active QMD input receives this config. +# Its native bare-date syntax matches the unchanged main564 macOS archive pattern and the retained root control. + +[[IgnoredVulns]] +id = "GHSA-vfj7-8cjw-p6xm" +ignoreUntil = 2000-01-01 +reason = "2026-10-03 bounded retirement review: this config is assigned only to blueprints/convergence-practice/wsl-retrieval/package-lock.json at SHA256 5c51ee65cc477f2c1488a38ff5cad1c0a737f81a5b61bbd70d5edc4d15bfc3bb. It preserves the original lock as historical evidence; braces 3.0.3 remains affected by GHSA-vfj7-8cjw-p6xm/CVE-2026-93687. Both old source/QMD runner modes stop before subprocess or output creation, and a dependency-free private manifest uses npm 11.19.0 devEngines.runtime.name=retired-wsl-retrieval/onFail=error to reject supported install/ci entry points. Original manifest/recording-aid bytes are archived and original receipts, failure facts and prior runner archives remain unchanged. This is a supported-entry-point guard, not an installation sandbox: explicit --force, other package managers and restored historical files are outside its claim. Source disposition and exact artifact bindings: blueprints/convergence-practice/wsl-retrieval/retirement-assessment.json. Independent native controls and retained failures: evidence/receipts/wsl-retrieval-retirement-20261003.json. Owner handoff: https://github.com/seathatflowsinourveins/native-agent-stack/pull/608#issuecomment-5963764481. The workflow and inventory tests constrain this one input; OSV itself does not constrain an explicit config to that path. Active QMD remains separately unresolved and ordinary/macOS configs carry no exception for this advisory. This exception expires at the native TOML date 2026-10-17; retirement does not patch a dependency or qualify active QMD." diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-expiry-control.exit.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-expiry-control.exit.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-expiry-control.exit.txt @@ -0,0 +1 @@ +1 diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-expiry-control.stderr.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-expiry-control.stderr.txt new file mode 100644 index 000000000..d4def2bcf --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-expiry-control.stderr.txt @@ -0,0 +1,5 @@ +Starting filesystem walk for root: / +Scanned ${REPOSITORY}/blueprints/convergence-practice/wsl-retrieval/package-lock.json file and found 170 packages +End status: 0 dirs visited, 1 inodes visited, 1 Extract calls, 6.651248ms elapsed, 6.651318ms wall time +${RUN_DIR}/expired-final-config.toml has unused ignores: + - GHSA-vfj7-8cjw-p6xm diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-expiry-control.stdout.json b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-expiry-control.stdout.json new file mode 100644 index 000000000..2a0f116ee --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-expiry-control.stdout.json @@ -0,0 +1,124 @@ +{ + "results": [ + { + "source": { + "path": "${REPOSITORY}/blueprints/convergence-practice/wsl-retrieval/package-lock.json", + "type": "lockfile" + }, + "packages": [ + { + "package": { + "name": "braces", + "version": "3.0.3", + "ecosystem": "npm" + }, + "groups": [ + { + "ids": [ + "GHSA-vfj7-8cjw-p6xm" + ], + "aliases": [ + "CVE-2026-93687", + "GHSA-vfj7-8cjw-p6xm" + ], + "max_severity": "8.7" + } + ], + "vulnerabilities": [ + { + "affected": [ + { + "database_specific": { + "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/09/GHSA-vfj7-8cjw-p6xm/GHSA-vfj7-8cjw-p6xm.json" + }, + "package": { + "ecosystem": "npm", + "name": "braces", + "purl": "pkg:npm/braces" + }, + "ranges": [ + { + "events": [ + { + "introduced": "0" + }, + { + "last_affected": "3.0.3" + } + ], + "type": "SEMVER" + } + ] + } + ], + "aliases": [ + "CVE-2026-93687" + ], + "database_specific": { + "cwe_ids": [ + "CWE-674" + ], + "github_reviewed": true, + "github_reviewed_at": "2026-10-02T22:36:33Z", + "nvd_published_at": "2026-09-18T16:17:15Z", + "severity": "HIGH" + }, + "details": "braces through 3.0.3 contains a stack overflow vulnerability in the recursive AST walkers that lack depth guards. Attackers can supply deeply nested brace patterns under the character limit to exhaust the call stack and terminate the Node.js process with an uncaught RangeError.", + "id": "GHSA-vfj7-8cjw-p6xm", + "modified": "2026-10-02T22:45:04.328737432Z", + "published": "2026-09-18T18:31:41Z", + "references": [ + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-93687" + }, + { + "type": "WEB", + "url": "https://github.com/micromatch/braces/issues/70" + }, + { + "type": "PACKAGE", + "url": "https://github.com/micromatch/braces" + }, + { + "type": "WEB", + "url": "https://github.com/micromatch/braces/blob/3.0.3/lib/compile.js#L49-L53" + }, + { + "type": "WEB", + "url": "https://github.com/micromatch/braces/blob/3.0.3/lib/expand.js#L102-L105" + }, + { + "type": "WEB", + "url": "https://github.com/micromatch/braces/blob/3.0.3/lib/parse.js#L38-L40" + }, + { + "type": "WEB", + "url": "https://www.vulncheck.com/advisories/braces-through-3.0.3-stack-overflow-via-deeply-nested-patterns" + } + ], + "schema_version": "1.9.0", + "severity": [ + { + "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H", + "type": "CVSS_V3" + }, + { + "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N", + "type": "CVSS_V4" + } + ], + "summary": "braces vulnerable to stack-exhaustion denial of service through deeply nested patterns" + } + ] + } + ] + } + ], + "experimental_config": { + "licenses": { + "summary": false, + "allowlist": null + } + } +} diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-integration.exit.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-integration.exit.txt new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-integration.exit.txt @@ -0,0 +1 @@ +0 diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-integration.stderr.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-integration.stderr.txt new file mode 100644 index 000000000..7363f4123 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-integration.stderr.txt @@ -0,0 +1,173 @@ +test_a_missing_allowed_lock_is_reported (tests.test_osv_lockfile_coverage.AllowedLockTests.test_a_missing_allowed_lock_is_reported) ... ok +test_allowed_advisories_are_scoped_active_ignores (tests.test_osv_lockfile_coverage.AllowedLockTests.test_allowed_advisories_are_scoped_active_ignores) ... ok +test_an_allowed_lock_is_exempt_only_for_the_advisories_it_lists (tests.test_osv_lockfile_coverage.AllowedLockTests.test_an_allowed_lock_is_exempt_only_for_the_advisories_it_lists) ... ok +test_an_ignore_is_active_only_before_its_ignore_until_date (tests.test_osv_lockfile_coverage.AllowedLockTests.test_an_ignore_is_active_only_before_its_ignore_until_date) ... ok +test_an_include_in_an_allowed_lock_is_flagged_and_not_exempt (tests.test_osv_lockfile_coverage.AllowedLockTests.test_an_include_in_an_allowed_lock_is_flagged_and_not_exempt) ... ok +test_every_allowed_lock_is_an_inventory_lockfile (tests.test_osv_lockfile_coverage.AllowedLockTests.test_every_allowed_lock_is_an_inventory_lockfile) ... ok +test_every_allowed_lock_is_self_contained (tests.test_osv_lockfile_coverage.AllowedLockTests.test_every_allowed_lock_is_self_contained) ... ok +test_every_allowed_lock_matches_its_reviewed_digest (tests.test_osv_lockfile_coverage.AllowedLockTests.test_every_allowed_lock_matches_its_reviewed_digest) ... ok +test_every_allowed_lock_names_existing_evidence (tests.test_osv_lockfile_coverage.AllowedLockTests.test_every_allowed_lock_names_existing_evidence) ... ok +test_the_digest_check_catches_a_changed_byte (tests.test_osv_lockfile_coverage.AllowedLockTests.test_the_digest_check_catches_a_changed_byte) ... ok +test_the_digest_check_reads_every_allowed_lock (tests.test_osv_lockfile_coverage.AllowedLockTests.test_the_digest_check_reads_every_allowed_lock) ... ok +test_the_digest_is_taken_over_bytes_not_text (tests.test_osv_lockfile_coverage.AllowedLockTests.test_the_digest_is_taken_over_bytes_not_text) ... ok +test_changed_lock_bytes_and_missing_evidence_are_rejected (tests.test_osv_lockfile_coverage.FrozenPolicyMutationTests.test_changed_lock_bytes_and_missing_evidence_are_rejected) ... ok +test_changed_retirement_status_and_assessment_lock_are_rejected (tests.test_osv_lockfile_coverage.FrozenPolicyMutationTests.test_changed_retirement_status_and_assessment_lock_are_rejected) ... ok +test_expired_config_is_rejected_by_the_actual_preflight_guard (tests.test_osv_lockfile_coverage.FrozenPolicyMutationTests.test_expired_config_is_rejected_by_the_actual_preflight_guard) ... ok +test_frozen_advisory_or_package_override_cannot_leak_to_ordinary_inputs (tests.test_osv_lockfile_coverage.FrozenPolicyMutationTests.test_frozen_advisory_or_package_override_cannot_leak_to_ordinary_inputs) ... ok +test_missing_reason_binding_or_extended_expiry_is_rejected (tests.test_osv_lockfile_coverage.FrozenPolicyMutationTests.test_missing_reason_binding_or_extended_expiry_is_rejected) ... ok +test_ordinary_inventory_omission_is_rejected_by_the_actual_preflight_guard (tests.test_osv_lockfile_coverage.FrozenPolicyMutationTests.test_ordinary_inventory_omission_is_rejected_by_the_actual_preflight_guard) ... ok +test_wrong_receipt_identity_or_artifact_binding_is_rejected (tests.test_osv_lockfile_coverage.FrozenPolicyMutationTests.test_wrong_receipt_identity_or_artifact_binding_is_rejected) ... ok +test_wsl_advisory_cannot_leak_into_the_macos_config (tests.test_osv_lockfile_coverage.FrozenPolicyMutationTests.test_wsl_advisory_cannot_leak_into_the_macos_config) ... ok +test_each_frozen_config_holds_exactly_the_advisories_of_its_locks (tests.test_osv_lockfile_coverage.FrozenScanTests.test_each_frozen_config_holds_exactly_the_advisories_of_its_locks) ... ok +test_each_frozen_lock_matches_its_reviewed_digest_and_names_evidence (tests.test_osv_lockfile_coverage.FrozenScanTests.test_each_frozen_lock_matches_its_reviewed_digest_and_names_evidence) ... ok +test_expired_ignore_fails_the_policy_before_a_scan (tests.test_osv_lockfile_coverage.FrozenScanTests.test_expired_ignore_fails_the_policy_before_a_scan) ... ok +test_only_the_frozen_locks_name_a_config (tests.test_osv_lockfile_coverage.FrozenScanTests.test_only_the_frozen_locks_name_a_config) ... ok +test_the_ordinary_config_has_no_exception_for_a_frozen_advisory (tests.test_osv_lockfile_coverage.FrozenScanTests.test_the_ordinary_config_has_no_exception_for_a_frozen_advisory) ... ok +test_the_partition_check_catches_a_mutant_inventory (tests.test_osv_lockfile_coverage.FrozenScanTests.test_the_partition_check_catches_a_mutant_inventory) ... ok +test_the_split_is_exhaustive_and_disjoint (tests.test_osv_lockfile_coverage.FrozenScanTests.test_the_split_is_exhaustive_and_disjoint) ... ok +test_the_workflow_scans_each_config_in_its_own_invocation (tests.test_osv_lockfile_coverage.FrozenScanTests.test_the_workflow_scans_each_config_in_its_own_invocation) ... ok +test_wsl_config_is_short_lived_and_names_its_evidence (tests.test_osv_lockfile_coverage.FrozenScanTests.test_wsl_config_is_short_lived_and_names_its_evidence) ... ok +test_wsl_lock_has_a_dedicated_config_and_fixed_identity (tests.test_osv_lockfile_coverage.FrozenScanTests.test_wsl_lock_has_a_dedicated_config_and_fixed_identity) ... ok +test_a_continuation_is_joined_before_a_requirement_is_read (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_a_continuation_is_joined_before_a_requirement_is_read) ... ok +test_a_tracked_version_that_is_not_a_plain_release_fails_closed (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_a_tracked_version_that_is_not_a_plain_release_fails_closed) ... ok +test_a_uv_lock_pin_is_read (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_a_uv_lock_pin_is_read) ... ok +test_an_include_that_names_no_file_fails_closed (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_an_include_that_names_no_file_fails_closed) ... ok +test_documented_options_that_name_no_package_pass (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_documented_options_that_name_no_package_pass) ... ok +test_equality_extras_markers_spacing_and_case_are_read (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_equality_extras_markers_spacing_and_case_are_read) ... ok +test_every_ignore_has_id_reason_and_a_near_expiry (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_every_ignore_has_id_reason_and_a_near_expiry) ... ok +test_includes_are_followed_relative_to_the_including_file (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_includes_are_followed_relative_to_the_including_file) ... ok +test_lines_the_guard_cannot_parse_fail_closed (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_lines_the_guard_cannot_parse_fail_closed) ... ok +test_no_other_suppression_mechanism_bypasses_the_policy (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_no_other_suppression_mechanism_bypasses_the_policy) ... ok +test_repo_wide_ignores_hide_nothing_outside_their_allowed_lock (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_repo_wide_ignores_hide_nothing_outside_their_allowed_lock) ... ok +test_the_ignore_field_check_catches_a_mutant (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_the_ignore_field_check_catches_a_mutant) ... ok +test_the_scope_guard_catches_a_mutant (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_the_scope_guard_catches_a_mutant) ... ok +test_entries_are_unique_existing_files (tests.test_osv_lockfile_coverage.LockfileInventoryTests.test_entries_are_unique_existing_files) ... ok +test_every_tracked_lockfile_and_manifest_is_listed (tests.test_osv_lockfile_coverage.LockfileInventoryTests.test_every_tracked_lockfile_and_manifest_is_listed) ... ok +test_exclusions_are_reasoned_fixtures_not_scanned_anywhere (tests.test_osv_lockfile_coverage.LockfileInventoryTests.test_exclusions_are_reasoned_fixtures_not_scanned_anywhere) ... ok +test_manifests_without_an_extractor_point_at_a_scanned_lockfile (tests.test_osv_lockfile_coverage.LockfileInventoryTests.test_manifests_without_an_extractor_point_at_a_scanned_lockfile) ... ok +test_parsers_are_explicit_where_osv_cannot_infer_them (tests.test_osv_lockfile_coverage.LockfileInventoryTests.test_parsers_are_explicit_where_osv_cannot_infer_them) ... ok +test_workflow_scans_the_inventory_with_the_config (tests.test_osv_lockfile_coverage.LockfileInventoryTests.test_workflow_scans_the_inventory_with_the_config) ... ok +test_each_primary_and_sarif_status_is_retained (tests.test_osv_lockfile_coverage.SyntheticWorkflowInvocationTests.test_each_primary_and_sarif_status_is_retained) ... ok +test_pr_collects_three_primary_statuses_without_sarif (tests.test_osv_lockfile_coverage.SyntheticWorkflowInvocationTests.test_pr_collects_three_primary_statuses_without_sarif) ... ok +test_preflight_failure_is_returned_before_scan (tests.test_osv_lockfile_coverage.SyntheticWorkflowInvocationTests.test_preflight_failure_is_returned_before_scan) ... ok +test_three_invocations_keep_every_input_and_parser_separate (tests.test_osv_lockfile_coverage.SyntheticWorkflowInvocationTests.test_three_invocations_keep_every_input_and_parser_separate) ... ok +test_unknown_duplicate_missing_and_wrong_archive_assignments_fail_before_scan (tests.test_osv_lockfile_coverage.SyntheticWorkflowInvocationTests.test_unknown_duplicate_missing_and_wrong_archive_assignments_fail_before_scan) ... ok +test_exact_argument_boundaries_and_cwd_retained_for_success_and_failure (tests.test_wsl_retrieval.ArchivedCommandRecordingTests.test_exact_argument_boundaries_and_cwd_retained_for_success_and_failure) ... ok +test_launch_error_retains_attempted_command (tests.test_wsl_retrieval.ArchivedCommandRecordingTests.test_launch_error_retains_attempted_command) ... ok +test_timeout_retains_started_command_and_partial_output (tests.test_wsl_retrieval.ArchivedCommandRecordingTests.test_timeout_retains_started_command_and_partial_output) ... ok +test_offline_cli_checks_retirement_without_qualifying_history (tests.test_wsl_retrieval.RetiredRunnerTests.test_offline_cli_checks_retirement_without_qualifying_history) ... ok +test_offline_cli_rejects_changed_recording_aid_archive (tests.test_wsl_retrieval.RetiredRunnerTests.test_offline_cli_rejects_changed_recording_aid_archive) ... ok +test_offline_cli_rejects_restored_install_routes (tests.test_wsl_retrieval.RetiredRunnerTests.test_offline_cli_rejects_restored_install_routes) ... ok +test_old_modes_fail_before_subprocess_or_output_creation (tests.test_wsl_retrieval.RetiredRunnerTests.test_old_modes_fail_before_subprocess_or_output_creation) ... ok +test_archived_runner_and_native_receipts_remain_original_bytes (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_archived_runner_and_native_receipts_remain_original_bytes) ... ok +test_ast_range_mismatch_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_ast_range_mismatch_rejected) ... ok +test_ast_source_bytes_mismatch_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_ast_source_bytes_mismatch_rejected) ... ok +test_bounded_get_cannot_return_extra_lines (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_bounded_get_cannot_return_extra_lines) ... ok +test_changed_frozen_input_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_changed_frozen_input_rejected) ... ok +test_changed_original_manifest_archive_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_changed_original_manifest_archive_rejected) ... ok +test_embeddings_or_model_artifact_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_embeddings_or_model_artifact_rejected) ... ok +test_error_is_not_successful_absence (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_error_is_not_successful_absence) ... ok +test_every_frozen_input_is_required_in_every_attempt (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_every_frozen_input_is_required_in_every_attempt) ... ok +test_failed_attempt_cannot_add_an_unrelated_check (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_failed_attempt_cannot_add_an_unrelated_check) ... ok +test_incomplete_evidence_does_not_qualify_component_or_adoption (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_incomplete_evidence_does_not_qualify_component_or_adoption) ... ok +test_lexical_byte_span_mismatch_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_lexical_byte_span_mismatch_rejected) ... ok +test_lexical_source_line_mismatch_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_lexical_source_line_mismatch_rejected) ... ok +test_missing_body_or_wrong_line_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_missing_body_or_wrong_line_rejected) ... ok +test_missing_failed_commands_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_missing_failed_commands_rejected) ... ok +test_negative_cannot_return_cross_scope_content (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_negative_cannot_return_cross_scope_content) ... ok +test_optional_inference_backend_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_optional_inference_backend_rejected) ... ok +test_original_manifest_is_resolved_without_rewriting_receipt_names (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_original_manifest_is_resolved_without_rewriting_receipt_names) ... ok +test_recorded_native_outputs_pass (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_recorded_native_outputs_pass) ... ok +test_retained_failure_cannot_be_erased (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_retained_failure_cannot_be_erased) ... ok +test_same_count_check_substitution_rejected_for_every_passed_check (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_same_count_check_substitution_rejected_for_every_passed_check) ... ok +test_same_count_frozen_input_substitution_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_same_count_frozen_input_substitution_rejected) ... ok +test_stale_content_after_update_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_stale_content_after_update_rejected) ... ok +test_unknown_usage_or_global_scope_claim_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_unknown_usage_or_global_scope_claim_rejected) ... ok +test_unrelated_failure_cannot_be_relabeled_uri_compatibility (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_unrelated_failure_cannot_be_relabeled_uri_compatibility) ... ok +test_wrong_named_index_or_collection_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_wrong_named_index_or_collection_rejected) ... ok +test_every_uses_is_github_owned_or_in_the_pattern_allow_list (tests.test_workflow_hardening.ActionsAllowListTests.test_every_uses_is_github_owned_or_in_the_pattern_allow_list) ... ok +test_settings_target_selected_actions_with_sha_pinning_required (tests.test_workflow_hardening.ActionsAllowListTests.test_settings_target_selected_actions_with_sha_pinning_required) ... ok +test_bootstrap_jobs_stay_path_gated_on_pull_request_and_still_run_off_it (tests.test_workflow_hardening.AdoptionBootstrapMacosRequiredTests.test_bootstrap_jobs_stay_path_gated_on_pull_request_and_still_run_off_it) ... ok +test_changes_job_fails_safe_on_missing_shas_and_git_diff_errors (tests.test_workflow_hardening.AdoptionBootstrapMacosRequiredTests.test_changes_job_fails_safe_on_missing_shas_and_git_diff_errors) ... ok +test_changes_job_runs_only_on_pull_request_and_diffs_paths_matching_push (tests.test_workflow_hardening.AdoptionBootstrapMacosRequiredTests.test_changes_job_runs_only_on_pull_request_and_diffs_paths_matching_push) ... ok +test_pull_request_trigger_has_no_path_filter (tests.test_workflow_hardening.AdoptionBootstrapMacosRequiredTests.test_pull_request_trigger_has_no_path_filter) ... ok +test_the_pre_fix_condition_text_fails_this_tests_own_assertions (tests.test_workflow_hardening.AdoptionBootstrapMacosRequiredTests.test_the_pre_fix_condition_text_fails_this_tests_own_assertions) ... ok +test_validate_macos_is_reachable_on_every_pull_request (tests.test_workflow_hardening.AdoptionBootstrapMacosRequiredTests.test_validate_macos_is_reachable_on_every_pull_request) ... ok +test_blocks_history_rewrite_on_both_agent_prefix_patterns_without_bypass (tests.test_workflow_hardening.AgentBranchRulesetTests.test_blocks_history_rewrite_on_both_agent_prefix_patterns_without_bypass) ... ok +test_never_targets_main_or_blocks_post_merge_branch_deletion (tests.test_workflow_hardening.AgentBranchRulesetTests.test_never_targets_main_or_blocks_post_merge_branch_deletion) ... ok +test_binaries_run_only_after_verification (tests.test_workflow_hardening.BetterleaksTrialJobTests.test_binaries_run_only_after_verification) ... ok +test_findings_exit_0_through_the_scanners_own_option (tests.test_workflow_hardening.BetterleaksTrialJobTests.test_findings_exit_0_through_the_scanners_own_option) +Report-only through betterleaks's own option: each scan passes --exit-code 0 (cmd/root.go line 82, ... ok +test_fixture_step_fails_when_a_scan_does_not_complete_in_the_real_fixture_module (tests.test_workflow_hardening.BetterleaksTrialJobTests.test_fixture_step_fails_when_a_scan_does_not_complete_in_the_real_fixture_module) +Cross-family review (2026-09-28, P2): a scanner error must not pass the fixture step as a detection ... ok +test_fixture_step_reports_assertion_failures_and_fails_on_anything_else (tests.test_workflow_hardening.BetterleaksTrialJobTests.test_fixture_step_reports_assertion_failures_and_fails_on_anything_else) +Over a stand-in test module, the fixture step exits 0 when every problem is an assertion failure. ... ok +test_fixture_tests_leave_out_the_unredacted_history_class (tests.test_workflow_hardening.BetterleaksTrialJobTests.test_fixture_tests_leave_out_the_unredacted_history_class) +Exactly the three fixture classes run. GitleaksBranchAncestryHistoryTests is left out: the job's ... ok +test_is_not_a_required_context_and_cannot_be_forced_green (tests.test_workflow_hardening.BetterleaksTrialJobTests.test_is_not_a_required_context_and_cannot_be_forced_green) ... ok +test_later_steps_need_the_verified_install_and_both_scans_redact (tests.test_workflow_hardening.BetterleaksTrialJobTests.test_later_steps_need_the_verified_install_and_both_scans_redact) ... ok +test_no_suppression_channel_that_only_betterleaks_reads (tests.test_workflow_hardening.BetterleaksTrialJobTests.test_no_suppression_channel_that_only_betterleaks_reads) +betterleaks 1.8.1 reads three suppression channels that gitleaks does not: a .betterleaksignore ... ok +test_read_only_hardened_and_uploads_nothing (tests.test_workflow_hardening.BetterleaksTrialJobTests.test_read_only_hardened_and_uploads_nothing) ... ok +test_runs_bash_with_pipefail (tests.test_workflow_hardening.BetterleaksTrialJobTests.test_runs_bash_with_pipefail) +GitHub runs an unspecified shell as `bash -e {0}` and `shell: bash` as ... ok +test_scan_steps_fail_only_on_a_scan_error_and_count_findings_by_rule (tests.test_workflow_hardening.BetterleaksTrialJobTests.test_scan_steps_fail_only_on_a_scan_error_and_count_findings_by_rule) +With a stand-in scanner, each scan step exits 0 with findings or none and 1 on a scan error, and its ... ok +test_runs_on_pull_requests_only_and_fails_on_high (tests.test_workflow_hardening.DependencyReviewTests.test_runs_on_pull_requests_only_and_fails_on_high) ... ok +test_secret_scan_job_runs_the_gitleaks_config_tests_with_the_pinned_binary (tests.test_workflow_hardening.GitleaksConfigTestsRunInCI.test_secret_scan_job_runs_the_gitleaks_config_tests_with_the_pinned_binary) ... ok +test_adoption_bootstrap_macos_jobs_are_not_exempt (tests.test_workflow_hardening.HardenRunnerTests.test_adoption_bootstrap_macos_jobs_are_not_exempt) ... ok +test_every_ubuntu_and_macos_job_starts_with_harden_runner_in_audit_mode (tests.test_workflow_hardening.HardenRunnerTests.test_every_ubuntu_and_macos_job_starts_with_harden_runner_in_audit_mode) ... ok +test_formerly_exempt_workflows_stay_hardened (tests.test_workflow_hardening.HardenRunnerTests.test_formerly_exempt_workflows_stay_hardened) ... ok +test_github_automation_cites_only_existing_tests (tests.test_workflow_hardening.HardenRunnerTests.test_github_automation_cites_only_existing_tests) ... ok +test_harden_runner_never_blocks_egress (tests.test_workflow_hardening.HardenRunnerTests.test_harden_runner_never_blocks_egress) ... ok +test_hardware_profile_smoke_linux_job_stays_checked_despite_the_macos_job_exemption (tests.test_workflow_hardening.HardenRunnerTests.test_hardware_profile_smoke_linux_job_stays_checked_despite_the_macos_job_exemption) ... ok +test_hash_frozen_exemptions_are_still_pinned (tests.test_workflow_hardening.HardenRunnerTests.test_hash_frozen_exemptions_are_still_pinned) ... skipped 'no workflow is hash-frozen (empty since 2026-09-26)' +test_job_parser_matches_yaml_when_available (tests.test_workflow_hardening.HardenRunnerTests.test_job_parser_matches_yaml_when_available) ... skipped 'PyYAML not installed; the text parser is the only job source' +test_every_permissions_key_is_a_parsed_block (tests.test_workflow_hardening.NoWorkflowApprovesPullRequestsTests.test_every_permissions_key_is_a_parsed_block) ... ok +test_no_workflow_holds_actions_write (tests.test_workflow_hardening.NoWorkflowApprovesPullRequestsTests.test_no_workflow_holds_actions_write) ... ok +test_no_workflow_reviews_or_approves_a_pull_request (tests.test_workflow_hardening.NoWorkflowApprovesPullRequestsTests.test_no_workflow_reviews_or_approves_a_pull_request) ... ok +test_pull_requests_write_is_granted_only_to_the_propose_job (tests.test_workflow_hardening.NoWorkflowApprovesPullRequestsTests.test_pull_requests_write_is_granted_only_to_the_propose_job) ... ok +test_every_third_party_action_is_pinned_by_full_sha (tests.test_workflow_hardening.PinningTests.test_every_third_party_action_is_pinned_by_full_sha) ... ok +test_pin_comments_name_an_exact_release_outside_hash_frozen_workflows (tests.test_workflow_hardening.PinningTests.test_pin_comments_name_an_exact_release_outside_hash_frozen_workflows) ... ok +test_the_comment_check_rejects_a_major_only_or_missing_comment (tests.test_workflow_hardening.PinningTests.test_the_comment_check_rejects_a_major_only_or_missing_comment) ... ok +test_publish_outputs_name_existing_step_ids (tests.test_workflow_hardening.PublishReleaseTests.test_publish_outputs_name_existing_step_ids) ... ok +test_release_job_is_tag_only_after_publish_with_contents_write_only (tests.test_workflow_hardening.PublishReleaseTests.test_release_job_is_tag_only_after_publish_with_contents_write_only) ... ok +test_release_rechecks_attested_digests_and_attaches_files_at_creation (tests.test_workflow_hardening.PublishReleaseTests.test_release_rechecks_attested_digests_and_attaches_files_at_creation) ... ok +test_only_the_analysis_job_may_write_code_scanning_results (tests.test_workflow_hardening.ScorecardTests.test_only_the_analysis_job_may_write_code_scanning_results) ... ok +test_results_are_not_published (tests.test_workflow_hardening.ScorecardTests.test_results_are_not_published) ... ok +test_sarif_goes_to_code_scanning_and_stays_an_artifact (tests.test_workflow_hardening.ScorecardTests.test_sarif_goes_to_code_scanning_and_stays_an_artifact) ... ok +test_failure_path_semantics_keep_findings_uploadable (tests.test_workflow_hardening.SecurityScanTests.test_failure_path_semantics_keep_findings_uploadable) ... ok +test_jobs_check_out_without_persisted_credentials (tests.test_workflow_hardening.SecurityScanTests.test_jobs_check_out_without_persisted_credentials) ... ok +test_osv_scanner_fails_on_findings_and_uploads_sarif_off_pull_requests (tests.test_workflow_hardening.SecurityScanTests.test_osv_scanner_fails_on_findings_and_uploads_sarif_off_pull_requests) ... ok +test_the_write_token_never_reaches_an_installed_tool (tests.test_workflow_hardening.SecurityScanTests.test_the_write_token_never_reaches_an_installed_tool) ... ok +test_triggers_include_every_pull_request_without_a_path_filter (tests.test_workflow_hardening.SecurityScanTests.test_triggers_include_every_pull_request_without_a_path_filter) ... ok +test_write_scope_is_job_local_and_limited_to_security_events (tests.test_workflow_hardening.SecurityScanTests.test_write_scope_is_job_local_and_limited_to_security_events) ... ok +test_zizmor_online_skips_pull_requests_and_reports_without_failing (tests.test_workflow_hardening.SecurityScanTests.test_zizmor_online_skips_pull_requests_and_reports_without_failing) ... ok +test_every_grype_ignore_names_a_review_date_that_has_not_passed (tests.test_workflow_hardening.SupplyChainGateTests.test_every_grype_ignore_names_a_review_date_that_has_not_passed) ... ok +test_grype_policy_changes_trigger_the_gate_on_push_and_pull_request (tests.test_workflow_hardening.SupplyChainGateTests.test_grype_policy_changes_trigger_the_gate_on_push_and_pull_request) ... ok +test_strict_up_to_date_checks_stay_off_and_required_signatures_stays_out (tests.test_workflow_hardening.TargetRulesetTests.test_strict_up_to_date_checks_stay_off_and_required_signatures_stays_out) ... ok +test_target_requires_the_security_gates_from_github_actions (tests.test_workflow_hardening.TargetRulesetTests.test_target_requires_the_security_gates_from_github_actions) ... ok +test_both_ci_runs_audit_the_repository_root_so_dependabot_yml_is_collected (tests.test_workflow_hardening.ValidateZizmorGateTests.test_both_ci_runs_audit_the_repository_root_so_dependabot_yml_is_collected) ... ok +test_findings_fail_the_required_check (tests.test_workflow_hardening.ValidateZizmorGateTests.test_findings_fail_the_required_check) ... ok +test_online_audits_get_the_read_only_job_token (tests.test_workflow_hardening.ValidateZizmorGateTests.test_online_audits_get_the_read_only_job_token) ... ok +test_a_merge_commit_built_on_another_branch_fails_closed (tests.test_workflow_hardening.VerdictReviewGateTests.test_a_merge_commit_built_on_another_branch_fails_closed) ... ok +test_a_pull_request_into_another_branch_fails_closed (tests.test_workflow_hardening.VerdictReviewGateTests.test_a_pull_request_into_another_branch_fails_closed) ... ok +test_event_values_reach_the_gate_through_env_only (tests.test_workflow_hardening.VerdictReviewGateTests.test_event_values_reach_the_gate_through_env_only) ... ok +test_no_dangerous_trigger_is_added_for_the_residual (tests.test_workflow_hardening.VerdictReviewGateTests.test_no_dangerous_trigger_is_added_for_the_residual) ... ok +test_pull_request_base_is_the_merge_commits_first_parent_cross_checked (tests.test_workflow_hardening.VerdictReviewGateTests.test_pull_request_base_is_the_merge_commits_first_parent_cross_checked) ... ok +test_pull_request_head_must_be_the_merge_commit_of_the_payload_head (tests.test_workflow_hardening.VerdictReviewGateTests.test_pull_request_head_must_be_the_merge_commit_of_the_payload_head) ... ok +test_pull_request_re_runs_when_its_base_branch_changes (tests.test_workflow_hardening.VerdictReviewGateTests.test_pull_request_re_runs_when_its_base_branch_changes) ... ok +test_push_to_main_executes_the_gate_and_fails_closed_without_a_previous_commit (tests.test_workflow_hardening.VerdictReviewGateTests.test_push_to_main_executes_the_gate_and_fails_closed_without_a_previous_commit) ... ok +test_read_only_hardened_and_without_persisted_credentials (tests.test_workflow_hardening.VerdictReviewGateTests.test_read_only_hardened_and_without_persisted_credentials) ... ok +test_runs_on_every_pull_request_without_a_path_filter (tests.test_workflow_hardening.VerdictReviewGateTests.test_runs_on_every_pull_request_without_a_path_filter) ... ok +test_the_base_gate_runs_when_the_base_has_one (tests.test_workflow_hardening.VerdictReviewGateTests.test_the_base_gate_runs_when_the_base_has_one) ... ok +test_the_bootstrap_copy_runs_only_for_the_change_that_adds_the_gate (tests.test_workflow_hardening.VerdictReviewGateTests.test_the_bootstrap_copy_runs_only_for_the_change_that_adds_the_gate) ... ok +test_the_gate_that_runs_is_the_base_commits_copy (tests.test_workflow_hardening.VerdictReviewGateTests.test_the_gate_that_runs_is_the_base_commits_copy) ... ok +test_the_merge_commit_assertion_executes (tests.test_workflow_hardening.VerdictReviewGateTests.test_the_merge_commit_assertion_executes) ... ok +test_whole_suite_classification (tests.test_workflow_hardening.WholeSuiteJobsCheckOutFullHistory.test_whole_suite_classification) ... ok +test_whole_suite_jobs_set_fetch_depth_zero (tests.test_workflow_hardening.WholeSuiteJobsCheckOutFullHistory.test_whole_suite_jobs_set_fetch_depth_zero) ... ok + +---------------------------------------------------------------------- +Ran 161 tests in 5.464s + +OK (skipped=2) diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-integration.stdout.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-integration.stdout.txt new file mode 100644 index 000000000..e69de29bb diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-native-record.json b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-native-record.json new file mode 100644 index 000000000..37ad28ee3 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-native-record.json @@ -0,0 +1,280 @@ +{ + "schema_version": 1, + "evidence_class": "native_cli_integration_controls", + "source_base_revision": "56473e4b840f0e6940c031801d866e7e9bf29baf", + "scanner_revision": "e840a6e8adb14b7777c78e26cfbf6e2abc1d1fc6", + "scanner_binary_sha256": "ca69b3d3cd08f889a49dc0a383122f71cc528b83803671df5fd874d97485b108", + "workflow_run_block_sha256": "15ffbe6a824fb4bf041310488f17134adb79eadabda4a1407122d6f6a42c5d9e", + "input_bindings": [ + { + "path": ".github/workflows/security-scan.yml", + "bytes": 13965, + "sha256": "b7f1ba386dde30312b11713b41009a5fe75a9d52fd10a4b8db294eb24d207241" + }, + { + "path": ".github/osv-scanner-lockfiles.json", + "bytes": 10704, + "sha256": "51dae9e9907bda6c3a5de21c1c3dfdea79c5e0a379494ba4c6e42906abc6e114" + }, + { + "path": ".github/osv-scanner.toml", + "bytes": 7252, + "sha256": "dc6c8e8a535f78314525b8845d64f12bb743457147c9db0cae6d84c9297f9f87" + }, + { + "path": ".github/osv-scanner-frozen-macos.toml", + "bytes": 2547, + "sha256": "6b093b4320cc481c4b1d5722648ab37fba4a5b50d0553c62c936e1a78e0edbf2" + }, + { + "path": ".github/osv-scanner-frozen-wsl-retrieval.toml", + "bytes": 2330, + "sha256": "8931d59b82ce8326ea0028adef428a1262f3e16800d1b744341960513a139887" + }, + { + "path": "tests/test_osv_lockfile_coverage.py", + "bytes": 65234, + "sha256": "a5cb994e66138fdad5da76abaaf6e4645266b575eaf722cfb239cc78fe6d6da9" + }, + { + "path": "tests/test_wsl_retrieval.py", + "bytes": 18897, + "sha256": "b4e1abcc70ed75d302ca3ecabfba23bda9f53ab4a460a1d0fc16fd47453ea7fa" + }, + { + "path": "blueprints/convergence-practice/wsl-retrieval/retirement-assessment.json", + "bytes": 4162, + "sha256": "88f1919546dcf8b9fda7b425c7d3ea2ea114b975ccdc6356e0775a3f4e81a9f0" + }, + { + "path": "blueprints/convergence-practice/wsl-retrieval/package-lock.json", + "bytes": 82463, + "sha256": "5c51ee65cc477f2c1488a38ff5cad1c0a737f81a5b61bbd70d5edc4d15bfc3bb" + } + ], + "commands": [ + { + "case": "integration", + "argv": [ + "python3", + "-m", + "unittest", + "tests.test_osv_lockfile_coverage", + "tests.test_wsl_retrieval", + "tests.test_workflow_hardening", + "-v" + ], + "cwd": "${REPOSITORY}", + "exit_code": 0, + "tests_run": 161, + "skipped": 2 + }, + { + "case": "scan", + "argv": [ + "bash", + "--noprofile", + "--norc", + "-e", + "-o", + "pipefail", + "${RUN_DIR}/workflow-scan.sh" + ], + "cwd": "${REPOSITORY}", + "environment": { + "RUNNER_TEMP": "${RUN_DIR}/runner-temp", + "WRITE_SARIF": "true" + }, + "exit_code": 0, + "partition_counts": [ + 48, + 1, + 1 + ], + "primary_exit_codes": [ + 0, + 0, + 0 + ], + "sarif_exit_codes": [ + 0, + 0, + 0 + ], + "sarif_runs": [ + 1, + 1, + 1 + ], + "sarif_results": [ + 0, + 0, + 0 + ] + }, + { + "case": "active-control", + "argv": [ + "${OSV_RELEASE}/osv-scanner_linux_amd64", + "scan", + "source", + "--config", + ".github/osv-scanner.toml", + "--no-resolve", + "--format", + "json", + "--lockfile=${ACTIVE_CONTROL}/package-lock.json" + ], + "cwd": "${REPOSITORY}", + "exit_code": 1, + "advisory_ids": [ + "GHSA-vfj7-8cjw-p6xm" + ] + }, + { + "case": "expiry-control", + "argv": [ + "${OSV_RELEASE}/osv-scanner_linux_amd64", + "scan", + "source", + "--config", + "${RUN_DIR}/expired-final-config.toml", + "--no-resolve", + "--format", + "json", + "--lockfile=blueprints/convergence-practice/wsl-retrieval/package-lock.json" + ], + "cwd": "${REPOSITORY}", + "exit_code": 1, + "advisory_ids": [ + "GHSA-vfj7-8cjw-p6xm" + ] + }, + { + "case": "validate-before-scan", + "argv": [ + "python3", + "scripts/validate.py" + ], + "cwd": "${REPOSITORY}", + "exit_code": 1, + "finding": "Stale registration for already-empty integrated-osv-tests.stdout.txt; actual original and published bytes are empty." + } + ], + "output_files": [ + { + "path": "completion-workflow-scan.sh", + "bytes": 4308, + "sha256": "15ffbe6a824fb4bf041310488f17134adb79eadabda4a1407122d6f6a42c5d9e" + }, + { + "path": "completion-scan.stdout.txt", + "bytes": 18271, + "sha256": "4931d246717288ba22376c2c8a441134830c1a3849594cc6aac40bbf1c672d00" + }, + { + "path": "completion-scan.stderr.txt", + "bytes": 18215, + "sha256": "bb02db7b0614851f0847b822f0128259bab9afe37b68972499c3a9c603307209" + }, + { + "path": "completion-scan.exit.txt", + "bytes": 2, + "sha256": "9a271f2a916b0b6ee6cecb2426f0b3206ef074578be55d9bc94f6f3fe3ab86aa" + }, + { + "path": "completion-scan.time.txt", + "bytes": 786, + "sha256": "d2d4bfd44026a121efa8ad7b722c2c1af1aad0e264b29ccafb927d1fc99fbe80" + }, + { + "path": "completion-active-control.stdout.json", + "bytes": 4223, + "sha256": "4e08808f3ea453c25570a5fec746899c7ad4051564c167e5fe3505a167abe0d8" + }, + { + "path": "completion-active-control.stderr.txt", + "bytes": 350, + "sha256": "cea7753846753213dc346b68771a0426205224c064b69faca0b2df5059f100b1" + }, + { + "path": "completion-active-control.exit.txt", + "bytes": 2, + "sha256": "4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865" + }, + { + "path": "completion-expiry-control.stdout.json", + "bytes": 4265, + "sha256": "b34c8fcb7422d6bd507fb17bb4ca0da0bb982665d575ed005ebeb62da19367e1" + }, + { + "path": "completion-expiry-control.stderr.txt", + "bytes": 335, + "sha256": "5a4aad966dbd274a4dcc658af74a3b1a7adb53e7a635d01f081852aa71ea26ea" + }, + { + "path": "completion-expiry-control.exit.txt", + "bytes": 2, + "sha256": "4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865" + }, + { + "path": "completion-expired-final-config.toml", + "bytes": 2330, + "sha256": "4e9718c24a588f33b3d6d8d88ede1ec02c6bf8c8228303bfd057bd200299b143" + }, + { + "path": "completion-integration.stdout.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "completion-integration.stderr.txt", + "bytes": 28398, + "sha256": "ee349610c711adaa7a64da850fa7c8fd36a7447c8a3df2062acf446c4f119c21" + }, + { + "path": "completion-integration.exit.txt", + "bytes": 2, + "sha256": "9a271f2a916b0b6ee6cecb2426f0b3206ef074578be55d9bc94f6f3fe3ab86aa" + }, + { + "path": "completion-validate-before-scan.stdout.txt", + "bytes": 240, + "sha256": "cada4ef85786da5a8e8c4ae7486cac1cf47fc43f5ebf34c5fdc7356c34b6d36f" + }, + { + "path": "completion-validate-before-scan.stderr.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "completion-validate-before-scan.exit.txt", + "bytes": 2, + "sha256": "4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865" + }, + { + "path": "completion-osv-scanner-frozen-macos.sarif", + "bytes": 1168, + "sha256": "4568c81b00798436c2ec0a6ce67d41500aa483516d6b354f00bd1885c50b5955" + }, + { + "path": "completion-osv-scanner-frozen-wsl-retrieval.sarif", + "bytes": 1168, + "sha256": "4568c81b00798436c2ec0a6ce67d41500aa483516d6b354f00bd1885c50b5955" + }, + { + "path": "completion-osv-scanner.sarif", + "bytes": 1168, + "sha256": "4568c81b00798436c2ec0a6ce67d41500aa483516d6b354f00bd1885c50b5955" + } + ], + "sanitization": "Task and host paths replaced by role markers. Content and empty outputs otherwise preserved. SHA256 values in output_files bind sanitized published bytes; original outputs remain private. Exact argv from coordinator invocation; not a process trace. Native scan wrapped by RTK proxy and /usr/bin/time; scan.time retains the observed child command.", + "limitations": [ + "Local integration controls and native OSV operations, not upstream test suites or host/model/GPU acceptance.", + "The prior scan/controls remain separately preserved. This record supersedes their current-workflow wording only for the exact listed input bindings.", + "Active QMD still has the advisory. No active input gains this dedicated ignore; supported archive guard is not a general installation sandbox.", + "A preparation hash lookup targeted the osv260 directory and returned exit1; corrected to the released executable and verified its digest before scanning.", + "Provider usage is unknown; no provider inference is part of these checks." + ], + "whole_task_provider_usage": null +} diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-osv-scanner-frozen-macos.sarif b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-osv-scanner-frozen-macos.sarif new file mode 100644 index 000000000..566e1c2d1 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-osv-scanner-frozen-macos.sarif @@ -0,0 +1,47 @@ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "addresses": [], + "graphs": [], + "invocations": [], + "language": "en-US", + "logicalLocations": [], + "newlineSequences": [ + "\r\n", + "\n" + ], + "policies": [], + "redactionTokens": [], + "results": [], + "runAggregates": [], + "taxonomies": [], + "threadFlowLocations": [], + "tool": { + "driver": { + "contents": [ + "localizedData", + "nonLocalizedData" + ], + "informationUri": "https://github.com/google/osv-scanner", + "isComprehensive": false, + "language": "en-US", + "locations": [], + "name": "osv-scanner", + "notifications": [], + "rules": [], + "supportedTaxonomies": [], + "taxa": [], + "version": "2.6.0" + }, + "extensions": [] + }, + "translations": [], + "versionControlProvenance": [], + "webRequests": [], + "webResponses": [] + } + ], + "properties": {} +} diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-osv-scanner-frozen-wsl-retrieval.sarif b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-osv-scanner-frozen-wsl-retrieval.sarif new file mode 100644 index 000000000..566e1c2d1 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-osv-scanner-frozen-wsl-retrieval.sarif @@ -0,0 +1,47 @@ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "addresses": [], + "graphs": [], + "invocations": [], + "language": "en-US", + "logicalLocations": [], + "newlineSequences": [ + "\r\n", + "\n" + ], + "policies": [], + "redactionTokens": [], + "results": [], + "runAggregates": [], + "taxonomies": [], + "threadFlowLocations": [], + "tool": { + "driver": { + "contents": [ + "localizedData", + "nonLocalizedData" + ], + "informationUri": "https://github.com/google/osv-scanner", + "isComprehensive": false, + "language": "en-US", + "locations": [], + "name": "osv-scanner", + "notifications": [], + "rules": [], + "supportedTaxonomies": [], + "taxa": [], + "version": "2.6.0" + }, + "extensions": [] + }, + "translations": [], + "versionControlProvenance": [], + "webRequests": [], + "webResponses": [] + } + ], + "properties": {} +} diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-osv-scanner.sarif b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-osv-scanner.sarif new file mode 100644 index 000000000..566e1c2d1 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-osv-scanner.sarif @@ -0,0 +1,47 @@ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "addresses": [], + "graphs": [], + "invocations": [], + "language": "en-US", + "logicalLocations": [], + "newlineSequences": [ + "\r\n", + "\n" + ], + "policies": [], + "redactionTokens": [], + "results": [], + "runAggregates": [], + "taxonomies": [], + "threadFlowLocations": [], + "tool": { + "driver": { + "contents": [ + "localizedData", + "nonLocalizedData" + ], + "informationUri": "https://github.com/google/osv-scanner", + "isComprehensive": false, + "language": "en-US", + "locations": [], + "name": "osv-scanner", + "notifications": [], + "rules": [], + "supportedTaxonomies": [], + "taxa": [], + "version": "2.6.0" + }, + "extensions": [] + }, + "translations": [], + "versionControlProvenance": [], + "webRequests": [], + "webResponses": [] + } + ], + "properties": {} +} diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-scan.exit.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-scan.exit.txt new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-scan.exit.txt @@ -0,0 +1 @@ +0 diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-scan.stderr.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-scan.stderr.txt new file mode 100644 index 000000000..c6513e219 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-scan.stderr.txt @@ -0,0 +1,73 @@ +.................... +---------------------------------------------------------------------- +Ran 20 tests in 0.234s + +OK +Starting filesystem walk for root: / +Scanned ${REPOSITORY}/.github/requirements-ci.txt file and found 1 package +Scanned ${REPOSITORY}/blueprints/us-equities/sim-crosscheck-hftbacktest/requirements.lock file and found 43 packages +Scanned ${REPOSITORY}/adoption/sdk/accepted-constraints.txt file and found 36 packages +Scanned ${REPOSITORY}/adoption/sdk/requirements-linux-x86_64-py313.lock file and found 36 packages +Scanned ${REPOSITORY}/blueprints/convergence-practice/application-delivery/pnpm-lock.yaml file and found 109 packages +Scanned ${REPOSITORY}/blueprints/convergence-practice/application-delivery/uv.lock file and found 31 packages +Scanned ${REPOSITORY}/blueprints/memory-stack/native-qualification/uv.lock file and found 86 packages +Scanned ${REPOSITORY}/blueprints/us-equities/adaptive-paper/requirements.txt file and found 2 packages +Scanned ${REPOSITORY}/blueprints/us-equities/catalyst-provenance/requirements.lock file and found 42 packages +Scanned ${REPOSITORY}/blueprints/us-equities/data/requirements.in file and found 5 packages +Scanned ${REPOSITORY}/blueprints/us-equities/data/requirements.lock file and found 21 packages +Scanned ${REPOSITORY}/blueprints/runtime-workers/openhands/requirements.lock file and found 334 packages +Scanned ${REPOSITORY}/blueprints/runtime-workers/openhands/build-requirements.lock file and found 3 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/alpaca.packages.lock.json file and found 45 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Algorithm.CSharp/packages.lock.json file and found 30 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Algorithm.Framework/packages.lock.json file and found 26 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Algorithm/packages.lock.json file and found 22 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/AlgorithmFactory/packages.lock.json file and found 23 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Api/packages.lock.json file and found 22 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Brokerages/packages.lock.json file and found 24 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Common/packages.lock.json file and found 20 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Compression/packages.lock.json file and found 6 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Configuration/packages.lock.json file and found 5 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/DownloaderDataProvider/packages.lock.json file and found 37 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Engine/packages.lock.json file and found 36 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Indicators/packages.lock.json file and found 21 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Launcher/packages.lock.json file and found 81 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Logging/packages.lock.json file and found 1 package +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Messaging/packages.lock.json file and found 28 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Optimizer.Launcher/packages.lock.json file and found 22 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Optimizer/packages.lock.json file and found 21 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Queues/packages.lock.json file and found 25 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Report/packages.lock.json file and found 48 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Research/packages.lock.json file and found 68 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Tests/packages.lock.json file and found 97 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/ToolBox/packages.lock.json file and found 37 packages +Scanned ${REPOSITORY}/blueprints/us-equities/research-evaluation/requirements.in file and found 2 packages +Scanned ${REPOSITORY}/blueprints/us-equities/research-evaluation/requirements.lock file and found 19 packages +Scanned ${REPOSITORY}/blueprints/us-equities/workers/requirements.txt file and found 5 packages +Scanned ${REPOSITORY}/tools/mlx-smoke/requirements.in file and found 3 packages +Scanned ${REPOSITORY}/tools/mlx-smoke/requirements.lock.txt file and found 34 packages +Scanned ${REPOSITORY}/blueprints/gap-wave2-20260923/us-equities__portfolio-risk/requirements-libs.lock file and found 56 packages +Scanned ${REPOSITORY}/blueprints/us-equities/mover-v3/data-tools/requirements.lock file and found 9 packages +Scanned ${REPOSITORY}/blueprints/us-equities/mover-v3/study/runtime-requirements.txt file and found 10 packages +Scanned ${REPOSITORY}/evidence/artifacts/macos-syn-e2e-20260924/requirements-macos-arm64-py312.lock file and found 20 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine-trials/spy-one-zero-20260926/ml4t-backtest/lockcheck/ml4t.lock file and found 15 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine-trials/spy-one-zero-20260926/lumibot/lockcheck/lumibot.lock file and found 264 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine-trials/spy-one-zero-20260926/lumibot/lockcheck/ibapi-build/build.lock file and found 4 packages +End status: 0 dirs visited, 48 inodes visited, 48 Extract calls, 117.833436ms elapsed, 117.833496ms wall time +Filtered 175 local/unscannable package/s from the scan. +GHSA-hj66-6f7g-4r5v and 2 aliases have been filtered out because: oauthlib 0.6.1 through 3.3.1 (fixed in 4.0.0): RevocationEndpoint wraps its response in the request's callback parameter only when constructed with enable_jsonp=True (oauthlib/oauth2/rfc6749/endpoints/revocation.py:72-73 and 81-82 in 3.3.1); oauthlib's pre-configured servers never pass it. Two allowed locks pin 3.3.1: the OpenHands recipe lock and the frozen, evaluation-only Lumibot lock (run offline under bwrap). A text search of one hash-verified artifact per requirement of both locks found no enable_jsonp or create_revocation_response and no importer of oauthlib's endpoints, grant types or servers: oauthlib is imported only by requests-oauthlib 2.0.0 (client classes, token errors and helpers) and, in the Lumibot lock, kubernetes 36.0.3 (an exception class); every other RevocationEndpoint is Authlib's own class, and Authlib does not import oauthlib. Evidence: evidence/receipts/osv-oauthlib-pyjwt-reachability-20260929.json and, for the OpenHands lock at its relocked sha256, evidence/receipts/osv-openhands-pyjwt-relock-20260930.json. Expiry plan: the OpenHands lock was relocked onto PyJWT 2.14.0 on 2026-09-30 and still pins oauthlib 3.3.1; as of 2026-09-30, oauthlib 4.0.0 (PyPI, 2026-09-28T06:01Z) was inside the 7-day window the upstream workspace's exclude-newer applies (until 2026-10-05T06:01:19Z), so the relock onto it follows, tracked in #518 (owner: bc, session native-agent-stack-bc; earliest 2026-10-05T18:40:43Z, when PyJWT 2.15.1 has left its window too; due 2026-10-08), and in the same change deletes the OpenHands entry of IGNORE_ALLOWED_LOCKS, whose sha256 pin fails any change to that lock. The Lumibot lock stays frozen on 3.3.1, so on 2026-10-13 renew this ignore for that lock alone with a new dated reason, or delete that lock. +GHSA-xpv3-w29h-x7cv and 2 aliases have been filtered out because: oauthlib >= 3.0.0, < 4.0.0 (fixed in 4.0.0; the scanned locks pin 3.3.1): an oauthlib authorization server compares the PKCE code_verifier with == (code_challenge_method_s256 at oauthlib/oauth2/rfc6749/grant_types/authorization_code.py:49 and code_challenge_method_plain at :61, in 3.3.1), called only from AuthorizationCodeGrant.validate_token_request (line 506) when a token endpoint handles a request. Neither allowed lock (the OpenHands recipe lock and the frozen Lumibot lock) runs an oauthlib authorization server: in a text search of one hash-verified artifact per requirement of both locks, oauthlib is imported only by requests-oauthlib 2.0.0 (WebApplicationClient, LegacyApplicationClient, the oauth1 Client, token errors and helpers) and, in the Lumibot lock, kubernetes 36.0.3 (an exception class), and nothing imports oauthlib's grant types, endpoints or servers; the PKCE code in openhands-sdk, requests-oauthlib and google-auth-oauthlib only generates a verifier as a client. Evidence: evidence/receipts/osv-oauthlib-pyjwt-reachability-20260929.json and, for the OpenHands lock at its relocked sha256, evidence/receipts/osv-openhands-pyjwt-relock-20260930.json. Expiry plan: as for GHSA-hj66-6f7g-4r5v; the OpenHands relock onto oauthlib 4.0.0 removes that lock's pin, and on 2026-10-13 this ignore is renewed for the frozen Lumibot lock alone or that lock is deleted. +GHSA-8mgp-746c-j5xp and 2 aliases have been filtered out because: nltk through 3.10.3: TransitionParser.train/parse, AveragedPerceptron.save/load, PerceptronTagger.save_to_json and nltk.classify.maxent.save_maxent_params open caller-chosen model paths with the built-in open() instead of the pathsec helpers (the advisory's comparison case is the guarded PerceptronTagger.load_from_json, which in 3.10.3 still authorizes a caller-chosen private model directory), so untrusted input that chooses a model import or export path can read or write outside the allowed roots; pathsec enforcement is on by default (ENFORCE = True in nltk/pathsec.py of 3.10.3). No patched release as of 2026-09-27; the fixes are merged on develop (nltk #3757, #3759, #3813). Each lock allowed to pin nltk is named, with its reviewed sha256 and non-reachability evidence, in IGNORE_ALLOWED_LOCKS in tests/test_osv_lockfile_coverage.py. Lumibot lock: nltk 3.10.3 is a transitive pin (google-adk, llama-index-core) of the evaluation-only Lumibot 4.6.1 environment, run offline under bwrap, and no trial code calls these APIs (evidence: blueprints/us-equities/engine-trials/spy-one-zero-20260926/repository-checks.json). Expiry plan: relock every allowed lock onto the first nltk release that ships those fixes, then delete this ignore; delete the Lumibot lock unless a verdict has adopted Lumibot. +GHSA-hj66-6f7g-4r5v and 2 aliases have been filtered out because: oauthlib 0.6.1 through 3.3.1 (fixed in 4.0.0): RevocationEndpoint wraps its response in the request's callback parameter only when constructed with enable_jsonp=True (oauthlib/oauth2/rfc6749/endpoints/revocation.py:72-73 and 81-82 in 3.3.1); oauthlib's pre-configured servers never pass it. Two allowed locks pin 3.3.1: the OpenHands recipe lock and the frozen, evaluation-only Lumibot lock (run offline under bwrap). A text search of one hash-verified artifact per requirement of both locks found no enable_jsonp or create_revocation_response and no importer of oauthlib's endpoints, grant types or servers: oauthlib is imported only by requests-oauthlib 2.0.0 (client classes, token errors and helpers) and, in the Lumibot lock, kubernetes 36.0.3 (an exception class); every other RevocationEndpoint is Authlib's own class, and Authlib does not import oauthlib. Evidence: evidence/receipts/osv-oauthlib-pyjwt-reachability-20260929.json and, for the OpenHands lock at its relocked sha256, evidence/receipts/osv-openhands-pyjwt-relock-20260930.json. Expiry plan: the OpenHands lock was relocked onto PyJWT 2.14.0 on 2026-09-30 and still pins oauthlib 3.3.1; as of 2026-09-30, oauthlib 4.0.0 (PyPI, 2026-09-28T06:01Z) was inside the 7-day window the upstream workspace's exclude-newer applies (until 2026-10-05T06:01:19Z), so the relock onto it follows, tracked in #518 (owner: bc, session native-agent-stack-bc; earliest 2026-10-05T18:40:43Z, when PyJWT 2.15.1 has left its window too; due 2026-10-08), and in the same change deletes the OpenHands entry of IGNORE_ALLOWED_LOCKS, whose sha256 pin fails any change to that lock. The Lumibot lock stays frozen on 3.3.1, so on 2026-10-13 renew this ignore for that lock alone with a new dated reason, or delete that lock. +GHSA-xpv3-w29h-x7cv and 2 aliases have been filtered out because: oauthlib >= 3.0.0, < 4.0.0 (fixed in 4.0.0; the scanned locks pin 3.3.1): an oauthlib authorization server compares the PKCE code_verifier with == (code_challenge_method_s256 at oauthlib/oauth2/rfc6749/grant_types/authorization_code.py:49 and code_challenge_method_plain at :61, in 3.3.1), called only from AuthorizationCodeGrant.validate_token_request (line 506) when a token endpoint handles a request. Neither allowed lock (the OpenHands recipe lock and the frozen Lumibot lock) runs an oauthlib authorization server: in a text search of one hash-verified artifact per requirement of both locks, oauthlib is imported only by requests-oauthlib 2.0.0 (WebApplicationClient, LegacyApplicationClient, the oauth1 Client, token errors and helpers) and, in the Lumibot lock, kubernetes 36.0.3 (an exception class), and nothing imports oauthlib's grant types, endpoints or servers; the PKCE code in openhands-sdk, requests-oauthlib and google-auth-oauthlib only generates a verifier as a client. Evidence: evidence/receipts/osv-oauthlib-pyjwt-reachability-20260929.json and, for the OpenHands lock at its relocked sha256, evidence/receipts/osv-openhands-pyjwt-relock-20260930.json. Expiry plan: as for GHSA-hj66-6f7g-4r5v; the OpenHands relock onto oauthlib 4.0.0 removes that lock's pin, and on 2026-10-13 this ignore is renewed for the frozen Lumibot lock alone or that lock is deleted. +GHSA-h35f-9h28-mq5c and 3 aliases have been filtered out because: setuptools 80.10.2 in the same frozen evaluation-only lock, run offline under bwrap; the advisory needs an sdist build on macOS APFS/HFS+, while this environment was installed binary-only on Linux (--no-build) and its one source build used setuptools 84.0.0; relocking would change the recorded environment. At expiry, delete the Lumibot lock and these ignores unless Lumibot has been adopted by a verdict. Evidence: blueprints/us-equities/engine-trials/spy-one-zero-20260926/repository-checks.json +Filtered 11 vulnerabilities from output +Starting filesystem walk for root: / +Scanned ${REPOSITORY}/evidence/artifacts/macos-application-20260924/variant/pnpm-lock.yaml file and found 109 packages +End status: 0 dirs visited, 1 inodes visited, 1 Extract calls, 1.856003ms elapsed, 1.856063ms wall time +GHSA-vcvr-r3jv-pc5j has been filtered out because: next 16.2.0 through 16.3.5 (fixed in 16.3.6): the Node.js ImageResponse of next/og renders values that application code places into SVG content, attributes or styles, and attacker-controlled values there lead to remote code execution (GitHub advisory GHSA-vcvr-r3jv-pc5j, OSV record published 2026-09-30T14:48Z; the Edge implementation and applications that pass no such values are not affected, per the advisory). This config is applied only to the frozen macOS variant evidence/artifacts/macos-application-20260924/variant/pnpm-lock.yaml, which pins 16.3.5 and of which the repository keeps only package.json and this lock (two files, no application source). At 11227bfd, outside the artifact directory itself, git grep finds it only in the scan inventory, in the experiment record blueprints/convergence-practice/application-delivery/experiment-macos-20260924.json and in the evidence manifest (this change's tests, receipt and evidence name it too), and no workflow, script or recipe installs, builds or serves it, so no route can reach ImageResponse from anything this repository runs. The live Next/React recipe lock, blueprints/convergence-practice/application-delivery/pnpm-lock.yaml, pins 16.3.6, which the advisory does not affect, and is scanned under the other config, which has no exception for this advisory. The frozen lock cannot be bumped: its bytes are hash-bound evidence. Evidence: evidence/receipts/osv-urllib3-next-20260930.json. +Filtered 1 vulnerability from output +Starting filesystem walk for root: / +Scanned ${REPOSITORY}/blueprints/convergence-practice/wsl-retrieval/package-lock.json file and found 170 packages +End status: 0 dirs visited, 1 inodes visited, 1 Extract calls, 5.216508ms elapsed, 5.216578ms wall time +GHSA-vfj7-8cjw-p6xm and 1 alias have been filtered out because: 2026-10-03 bounded retirement review: this config is assigned only to blueprints/convergence-practice/wsl-retrieval/package-lock.json at SHA256 5c51ee65cc477f2c1488a38ff5cad1c0a737f81a5b61bbd70d5edc4d15bfc3bb. It preserves the original lock as historical evidence; braces 3.0.3 remains affected by GHSA-vfj7-8cjw-p6xm/CVE-2026-93687. Both old source/QMD runner modes stop before subprocess or output creation, and a dependency-free private manifest uses npm 11.19.0 devEngines.runtime.name=retired-wsl-retrieval/onFail=error to reject supported install/ci entry points. Original manifest/recording-aid bytes are archived and original receipts, failure facts and prior runner archives remain unchanged. This is a supported-entry-point guard, not an installation sandbox: explicit --force, other package managers and restored historical files are outside its claim. Source disposition and exact artifact bindings: blueprints/convergence-practice/wsl-retrieval/retirement-assessment.json. Independent native controls and retained failures: evidence/receipts/wsl-retrieval-retirement-20261003.json. Owner handoff: https://github.com/seathatflowsinourveins/native-agent-stack/pull/608#issuecomment-5963764481. The workflow and inventory tests constrain this one input; OSV itself does not constrain an explicit config to that path. Active QMD remains separately unresolved and ordinary/macOS configs carry no exception for this advisory. This exception expires at the native TOML date 2026-10-17; retirement does not patch a dependency or qualify active QMD. +Filtered 1 vulnerability from output diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-scan.stdout.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-scan.stdout.txt new file mode 100644 index 000000000..4610e2224 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-scan.stdout.txt @@ -0,0 +1,76 @@ +Starting filesystem walk for root: / +Scanned ${REPOSITORY}/.github/requirements-ci.txt file and found 1 package +Scanned ${REPOSITORY}/blueprints/us-equities/sim-crosscheck-hftbacktest/requirements.lock file and found 43 packages +Scanned ${REPOSITORY}/adoption/sdk/accepted-constraints.txt file and found 36 packages +Scanned ${REPOSITORY}/adoption/sdk/requirements-linux-x86_64-py313.lock file and found 36 packages +Scanned ${REPOSITORY}/blueprints/convergence-practice/application-delivery/pnpm-lock.yaml file and found 109 packages +Scanned ${REPOSITORY}/blueprints/convergence-practice/application-delivery/uv.lock file and found 31 packages +Scanned ${REPOSITORY}/blueprints/memory-stack/native-qualification/uv.lock file and found 86 packages +Scanned ${REPOSITORY}/blueprints/us-equities/adaptive-paper/requirements.txt file and found 2 packages +Scanned ${REPOSITORY}/blueprints/us-equities/catalyst-provenance/requirements.lock file and found 42 packages +Scanned ${REPOSITORY}/blueprints/us-equities/data/requirements.in file and found 5 packages +Scanned ${REPOSITORY}/blueprints/us-equities/data/requirements.lock file and found 21 packages +Scanned ${REPOSITORY}/blueprints/runtime-workers/openhands/requirements.lock file and found 334 packages +Scanned ${REPOSITORY}/blueprints/runtime-workers/openhands/build-requirements.lock file and found 3 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/alpaca.packages.lock.json file and found 45 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Algorithm.CSharp/packages.lock.json file and found 30 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Algorithm.Framework/packages.lock.json file and found 26 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Algorithm/packages.lock.json file and found 22 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/AlgorithmFactory/packages.lock.json file and found 23 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Api/packages.lock.json file and found 22 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Brokerages/packages.lock.json file and found 24 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Common/packages.lock.json file and found 20 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Compression/packages.lock.json file and found 6 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Configuration/packages.lock.json file and found 5 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/DownloaderDataProvider/packages.lock.json file and found 37 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Engine/packages.lock.json file and found 36 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Indicators/packages.lock.json file and found 21 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Launcher/packages.lock.json file and found 81 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Logging/packages.lock.json file and found 1 package +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Messaging/packages.lock.json file and found 28 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Optimizer.Launcher/packages.lock.json file and found 22 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Optimizer/packages.lock.json file and found 21 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Queues/packages.lock.json file and found 25 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Report/packages.lock.json file and found 48 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Research/packages.lock.json file and found 68 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/Tests/packages.lock.json file and found 97 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine/patches/lean-locks/ToolBox/packages.lock.json file and found 37 packages +Scanned ${REPOSITORY}/blueprints/us-equities/research-evaluation/requirements.in file and found 2 packages +Scanned ${REPOSITORY}/blueprints/us-equities/research-evaluation/requirements.lock file and found 19 packages +Scanned ${REPOSITORY}/blueprints/us-equities/workers/requirements.txt file and found 5 packages +Scanned ${REPOSITORY}/tools/mlx-smoke/requirements.in file and found 3 packages +Scanned ${REPOSITORY}/tools/mlx-smoke/requirements.lock.txt file and found 34 packages +Scanned ${REPOSITORY}/blueprints/gap-wave2-20260923/us-equities__portfolio-risk/requirements-libs.lock file and found 56 packages +Scanned ${REPOSITORY}/blueprints/us-equities/mover-v3/data-tools/requirements.lock file and found 9 packages +Scanned ${REPOSITORY}/blueprints/us-equities/mover-v3/study/runtime-requirements.txt file and found 10 packages +Scanned ${REPOSITORY}/evidence/artifacts/macos-syn-e2e-20260924/requirements-macos-arm64-py312.lock file and found 20 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine-trials/spy-one-zero-20260926/ml4t-backtest/lockcheck/ml4t.lock file and found 15 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine-trials/spy-one-zero-20260926/lumibot/lockcheck/lumibot.lock file and found 264 packages +Scanned ${REPOSITORY}/blueprints/us-equities/engine-trials/spy-one-zero-20260926/lumibot/lockcheck/ibapi-build/build.lock file and found 4 packages +End status: 0 dirs visited, 48 inodes visited, 48 Extract calls, 108.519571ms elapsed, 108.519631ms wall time +Filtered 175 local/unscannable package/s from the scan. +GHSA-hj66-6f7g-4r5v and 2 aliases have been filtered out because: oauthlib 0.6.1 through 3.3.1 (fixed in 4.0.0): RevocationEndpoint wraps its response in the request's callback parameter only when constructed with enable_jsonp=True (oauthlib/oauth2/rfc6749/endpoints/revocation.py:72-73 and 81-82 in 3.3.1); oauthlib's pre-configured servers never pass it. Two allowed locks pin 3.3.1: the OpenHands recipe lock and the frozen, evaluation-only Lumibot lock (run offline under bwrap). A text search of one hash-verified artifact per requirement of both locks found no enable_jsonp or create_revocation_response and no importer of oauthlib's endpoints, grant types or servers: oauthlib is imported only by requests-oauthlib 2.0.0 (client classes, token errors and helpers) and, in the Lumibot lock, kubernetes 36.0.3 (an exception class); every other RevocationEndpoint is Authlib's own class, and Authlib does not import oauthlib. Evidence: evidence/receipts/osv-oauthlib-pyjwt-reachability-20260929.json and, for the OpenHands lock at its relocked sha256, evidence/receipts/osv-openhands-pyjwt-relock-20260930.json. Expiry plan: the OpenHands lock was relocked onto PyJWT 2.14.0 on 2026-09-30 and still pins oauthlib 3.3.1; as of 2026-09-30, oauthlib 4.0.0 (PyPI, 2026-09-28T06:01Z) was inside the 7-day window the upstream workspace's exclude-newer applies (until 2026-10-05T06:01:19Z), so the relock onto it follows, tracked in #518 (owner: bc, session native-agent-stack-bc; earliest 2026-10-05T18:40:43Z, when PyJWT 2.15.1 has left its window too; due 2026-10-08), and in the same change deletes the OpenHands entry of IGNORE_ALLOWED_LOCKS, whose sha256 pin fails any change to that lock. The Lumibot lock stays frozen on 3.3.1, so on 2026-10-13 renew this ignore for that lock alone with a new dated reason, or delete that lock. +GHSA-xpv3-w29h-x7cv and 2 aliases have been filtered out because: oauthlib >= 3.0.0, < 4.0.0 (fixed in 4.0.0; the scanned locks pin 3.3.1): an oauthlib authorization server compares the PKCE code_verifier with == (code_challenge_method_s256 at oauthlib/oauth2/rfc6749/grant_types/authorization_code.py:49 and code_challenge_method_plain at :61, in 3.3.1), called only from AuthorizationCodeGrant.validate_token_request (line 506) when a token endpoint handles a request. Neither allowed lock (the OpenHands recipe lock and the frozen Lumibot lock) runs an oauthlib authorization server: in a text search of one hash-verified artifact per requirement of both locks, oauthlib is imported only by requests-oauthlib 2.0.0 (WebApplicationClient, LegacyApplicationClient, the oauth1 Client, token errors and helpers) and, in the Lumibot lock, kubernetes 36.0.3 (an exception class), and nothing imports oauthlib's grant types, endpoints or servers; the PKCE code in openhands-sdk, requests-oauthlib and google-auth-oauthlib only generates a verifier as a client. Evidence: evidence/receipts/osv-oauthlib-pyjwt-reachability-20260929.json and, for the OpenHands lock at its relocked sha256, evidence/receipts/osv-openhands-pyjwt-relock-20260930.json. Expiry plan: as for GHSA-hj66-6f7g-4r5v; the OpenHands relock onto oauthlib 4.0.0 removes that lock's pin, and on 2026-10-13 this ignore is renewed for the frozen Lumibot lock alone or that lock is deleted. +GHSA-8mgp-746c-j5xp and 2 aliases have been filtered out because: nltk through 3.10.3: TransitionParser.train/parse, AveragedPerceptron.save/load, PerceptronTagger.save_to_json and nltk.classify.maxent.save_maxent_params open caller-chosen model paths with the built-in open() instead of the pathsec helpers (the advisory's comparison case is the guarded PerceptronTagger.load_from_json, which in 3.10.3 still authorizes a caller-chosen private model directory), so untrusted input that chooses a model import or export path can read or write outside the allowed roots; pathsec enforcement is on by default (ENFORCE = True in nltk/pathsec.py of 3.10.3). No patched release as of 2026-09-27; the fixes are merged on develop (nltk #3757, #3759, #3813). Each lock allowed to pin nltk is named, with its reviewed sha256 and non-reachability evidence, in IGNORE_ALLOWED_LOCKS in tests/test_osv_lockfile_coverage.py. Lumibot lock: nltk 3.10.3 is a transitive pin (google-adk, llama-index-core) of the evaluation-only Lumibot 4.6.1 environment, run offline under bwrap, and no trial code calls these APIs (evidence: blueprints/us-equities/engine-trials/spy-one-zero-20260926/repository-checks.json). Expiry plan: relock every allowed lock onto the first nltk release that ships those fixes, then delete this ignore; delete the Lumibot lock unless a verdict has adopted Lumibot. +GHSA-hj66-6f7g-4r5v and 2 aliases have been filtered out because: oauthlib 0.6.1 through 3.3.1 (fixed in 4.0.0): RevocationEndpoint wraps its response in the request's callback parameter only when constructed with enable_jsonp=True (oauthlib/oauth2/rfc6749/endpoints/revocation.py:72-73 and 81-82 in 3.3.1); oauthlib's pre-configured servers never pass it. Two allowed locks pin 3.3.1: the OpenHands recipe lock and the frozen, evaluation-only Lumibot lock (run offline under bwrap). A text search of one hash-verified artifact per requirement of both locks found no enable_jsonp or create_revocation_response and no importer of oauthlib's endpoints, grant types or servers: oauthlib is imported only by requests-oauthlib 2.0.0 (client classes, token errors and helpers) and, in the Lumibot lock, kubernetes 36.0.3 (an exception class); every other RevocationEndpoint is Authlib's own class, and Authlib does not import oauthlib. Evidence: evidence/receipts/osv-oauthlib-pyjwt-reachability-20260929.json and, for the OpenHands lock at its relocked sha256, evidence/receipts/osv-openhands-pyjwt-relock-20260930.json. Expiry plan: the OpenHands lock was relocked onto PyJWT 2.14.0 on 2026-09-30 and still pins oauthlib 3.3.1; as of 2026-09-30, oauthlib 4.0.0 (PyPI, 2026-09-28T06:01Z) was inside the 7-day window the upstream workspace's exclude-newer applies (until 2026-10-05T06:01:19Z), so the relock onto it follows, tracked in #518 (owner: bc, session native-agent-stack-bc; earliest 2026-10-05T18:40:43Z, when PyJWT 2.15.1 has left its window too; due 2026-10-08), and in the same change deletes the OpenHands entry of IGNORE_ALLOWED_LOCKS, whose sha256 pin fails any change to that lock. The Lumibot lock stays frozen on 3.3.1, so on 2026-10-13 renew this ignore for that lock alone with a new dated reason, or delete that lock. +GHSA-xpv3-w29h-x7cv and 2 aliases have been filtered out because: oauthlib >= 3.0.0, < 4.0.0 (fixed in 4.0.0; the scanned locks pin 3.3.1): an oauthlib authorization server compares the PKCE code_verifier with == (code_challenge_method_s256 at oauthlib/oauth2/rfc6749/grant_types/authorization_code.py:49 and code_challenge_method_plain at :61, in 3.3.1), called only from AuthorizationCodeGrant.validate_token_request (line 506) when a token endpoint handles a request. Neither allowed lock (the OpenHands recipe lock and the frozen Lumibot lock) runs an oauthlib authorization server: in a text search of one hash-verified artifact per requirement of both locks, oauthlib is imported only by requests-oauthlib 2.0.0 (WebApplicationClient, LegacyApplicationClient, the oauth1 Client, token errors and helpers) and, in the Lumibot lock, kubernetes 36.0.3 (an exception class), and nothing imports oauthlib's grant types, endpoints or servers; the PKCE code in openhands-sdk, requests-oauthlib and google-auth-oauthlib only generates a verifier as a client. Evidence: evidence/receipts/osv-oauthlib-pyjwt-reachability-20260929.json and, for the OpenHands lock at its relocked sha256, evidence/receipts/osv-openhands-pyjwt-relock-20260930.json. Expiry plan: as for GHSA-hj66-6f7g-4r5v; the OpenHands relock onto oauthlib 4.0.0 removes that lock's pin, and on 2026-10-13 this ignore is renewed for the frozen Lumibot lock alone or that lock is deleted. +GHSA-h35f-9h28-mq5c and 3 aliases have been filtered out because: setuptools 80.10.2 in the same frozen evaluation-only lock, run offline under bwrap; the advisory needs an sdist build on macOS APFS/HFS+, while this environment was installed binary-only on Linux (--no-build) and its one source build used setuptools 84.0.0; relocking would change the recorded environment. At expiry, delete the Lumibot lock and these ignores unless Lumibot has been adopted by a verdict. Evidence: blueprints/us-equities/engine-trials/spy-one-zero-20260926/repository-checks.json +Filtered 11 vulnerabilities from output + +No issues found +Starting filesystem walk for root: / +Scanned ${REPOSITORY}/evidence/artifacts/macos-application-20260924/variant/pnpm-lock.yaml file and found 109 packages +End status: 0 dirs visited, 1 inodes visited, 1 Extract calls, 1.912817ms elapsed, 1.912887ms wall time +GHSA-vcvr-r3jv-pc5j has been filtered out because: next 16.2.0 through 16.3.5 (fixed in 16.3.6): the Node.js ImageResponse of next/og renders values that application code places into SVG content, attributes or styles, and attacker-controlled values there lead to remote code execution (GitHub advisory GHSA-vcvr-r3jv-pc5j, OSV record published 2026-09-30T14:48Z; the Edge implementation and applications that pass no such values are not affected, per the advisory). This config is applied only to the frozen macOS variant evidence/artifacts/macos-application-20260924/variant/pnpm-lock.yaml, which pins 16.3.5 and of which the repository keeps only package.json and this lock (two files, no application source). At 11227bfd, outside the artifact directory itself, git grep finds it only in the scan inventory, in the experiment record blueprints/convergence-practice/application-delivery/experiment-macos-20260924.json and in the evidence manifest (this change's tests, receipt and evidence name it too), and no workflow, script or recipe installs, builds or serves it, so no route can reach ImageResponse from anything this repository runs. The live Next/React recipe lock, blueprints/convergence-practice/application-delivery/pnpm-lock.yaml, pins 16.3.6, which the advisory does not affect, and is scanned under the other config, which has no exception for this advisory. The frozen lock cannot be bumped: its bytes are hash-bound evidence. Evidence: evidence/receipts/osv-urllib3-next-20260930.json. +Filtered 1 vulnerability from output + +No issues found +Starting filesystem walk for root: / +Scanned ${REPOSITORY}/blueprints/convergence-practice/wsl-retrieval/package-lock.json file and found 170 packages +End status: 0 dirs visited, 1 inodes visited, 1 Extract calls, 5.275765ms elapsed, 5.275825ms wall time +GHSA-vfj7-8cjw-p6xm and 1 alias have been filtered out because: 2026-10-03 bounded retirement review: this config is assigned only to blueprints/convergence-practice/wsl-retrieval/package-lock.json at SHA256 5c51ee65cc477f2c1488a38ff5cad1c0a737f81a5b61bbd70d5edc4d15bfc3bb. It preserves the original lock as historical evidence; braces 3.0.3 remains affected by GHSA-vfj7-8cjw-p6xm/CVE-2026-93687. Both old source/QMD runner modes stop before subprocess or output creation, and a dependency-free private manifest uses npm 11.19.0 devEngines.runtime.name=retired-wsl-retrieval/onFail=error to reject supported install/ci entry points. Original manifest/recording-aid bytes are archived and original receipts, failure facts and prior runner archives remain unchanged. This is a supported-entry-point guard, not an installation sandbox: explicit --force, other package managers and restored historical files are outside its claim. Source disposition and exact artifact bindings: blueprints/convergence-practice/wsl-retrieval/retirement-assessment.json. Independent native controls and retained failures: evidence/receipts/wsl-retrieval-retirement-20261003.json. Owner handoff: https://github.com/seathatflowsinourveins/native-agent-stack/pull/608#issuecomment-5963764481. The workflow and inventory tests constrain this one input; OSV itself does not constrain an explicit config to that path. Active QMD remains separately unresolved and ordinary/macOS configs carry no exception for this advisory. This exception expires at the native TOML date 2026-10-17; retirement does not patch a dependency or qualify active QMD. +Filtered 1 vulnerability from output + +No issues found +OSV primary exit codes: ordinary=0 frozen-macos=0 frozen-wsl=0 +OSV SARIF exit codes: ordinary=0 frozen-macos=0 frozen-wsl=0 diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-scan.time.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-scan.time.txt new file mode 100644 index 000000000..9ed8d8c01 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-scan.time.txt @@ -0,0 +1,23 @@ + Command being timed: "bash --noprofile --norc -e -o pipefail ${RUN_DIR}/workflow-scan.sh" + User time (seconds): 1.03 + System time (seconds): 0.31 + Percent of CPU this job got: 24% + Elapsed (wall clock) time (h:mm:ss or m:ss): 0:05.40 + Average shared text size (kbytes): 0 + Average unshared data size (kbytes): 0 + Average stack size (kbytes): 0 + Average total size (kbytes): 0 + Maximum resident set size (kbytes): 64244 + Average resident set size (kbytes): 0 + Major (requiring I/O) page faults: 0 + Minor (reclaiming a frame) page faults: 59878 + Voluntary context switches: 5944 + Involuntary context switches: 54 + Swaps: 0 + File system inputs: 0 + File system outputs: 0 + Socket messages sent: 0 + Socket messages received: 0 + Signals delivered: 0 + Page size (bytes): 4096 + Exit status: 0 diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-validate-before-scan.exit.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-validate-before-scan.exit.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-validate-before-scan.exit.txt @@ -0,0 +1 @@ +1 diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-validate-before-scan.stderr.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-validate-before-scan.stderr.txt new file mode 100644 index 000000000..e69de29bb diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-validate-before-scan.stdout.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-validate-before-scan.stdout.txt new file mode 100644 index 000000000..5b83ac573 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-validate-before-scan.stdout.txt @@ -0,0 +1,3 @@ +Publication validation failed: +evidence/artifacts/wsl-retrieval-retirement-20261003/integrated-osv-tests.stdout.txt: SHA-256 mismatch +evidence/artifacts/wsl-retrieval-retirement-20261003/integrated-osv-tests.stdout.txt: byte count mismatch diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-workflow-scan.sh b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-workflow-scan.sh new file mode 100644 index 000000000..fba252a53 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/completion-workflow-scan.sh @@ -0,0 +1,67 @@ +# `shell: bash` runs with -e; turn it off so a findings exit (1) is captured and +# all three SARIF results are still written, then fail with the worst observed status. +set +e -u -o pipefail +inventory=.github/osv-scanner-lockfiles.json +frozen_config=.github/osv-scanner-frozen-macos.toml +frozen_wsl_config=.github/osv-scanner-frozen-wsl-retrieval.toml +# Scope comes from this caller, not OSV's explicit-config mechanism. Reject missing/duplicate inputs, +# config drift, changed lock digests, missing evidence, expired policy and restored replay/install routes. +python3 -m unittest \ + tests.test_osv_lockfile_coverage.LockfileInventoryTests \ + tests.test_osv_lockfile_coverage.FrozenScanTests \ + tests.test_wsl_retrieval.RetiredRunnerTests || exit "$?" +# An entry without a parser lets OSV-Scanner infer it from the file name (":"). +# An explicit --config applies to every input of one invocation (OSV-Scanner docs/configuration.md), +# so each frozen group has its own invocation, and ordinary entries use the config with no archive exception. +mapfile -t lockfiles < <(jq -r '.lockfiles[] | select(has("config") | not) | "--lockfile=\(.parser // ""):\(.path)"' "$inventory") +mapfile -t frozen < <(jq -r --arg config "$frozen_config" '.lockfiles[] | select(.config == $config) | "--lockfile=\(.parser // ""):\(.path)"' "$inventory") +mapfile -t frozen_wsl < <(jq -r --arg config "$frozen_wsl_config" '.lockfiles[] | select(.config == $config) | "--lockfile=\(.parser // ""):\(.path)"' "$inventory") +test "${#lockfiles[@]}" -gt 0 || exit 1 +test "${#frozen[@]}" -gt 0 || exit 1 +test "${#frozen_wsl[@]}" -gt 0 || exit 1 +# Every entry is in exactly one scan: together the three lists are the inventory, so an entry that +# names any other config is in neither and fails here. +test "$(( ${#lockfiles[@]} + ${#frozen[@]} + ${#frozen_wsl[@]} ))" -eq "$(jq '.lockfiles | length' "$inventory")" || exit 1 +# Also enforce unique inputs and the two exact archive assignments in the shell caller itself. +jq -e --arg mac_config "$frozen_config" --arg wsl_config "$frozen_wsl_config" ' + .lockfiles as $entries | ($entries | map(.path)) as $paths | + (($paths | length) == ($paths | unique | length)) and + ([$entries[] | select(.config == $mac_config) | .path] == + ["evidence/artifacts/macos-application-20260924/variant/pnpm-lock.yaml"]) and + ([$entries[] | select(.config == $wsl_config) | .path] == + ["blueprints/convergence-practice/wsl-retrieval/package-lock.json"]) +' "$inventory" > /dev/null || exit 1 +# --no-resolve: scan the versions each file pins. Transitive resolution of the +# unlocked manifests reported versions no lockfile installs (decision record). +scan=("$RUNNER_TEMP/osv-scanner/osv-scanner" scan source --config .github/osv-scanner.toml + --no-resolve "${lockfiles[@]}") +scan_frozen=("$RUNNER_TEMP/osv-scanner/osv-scanner" scan source --config "$frozen_config" + --no-resolve "${frozen[@]}") +scan_frozen_wsl=("$RUNNER_TEMP/osv-scanner/osv-scanner" scan source --config "$frozen_wsl_config" + --no-resolve "${frozen_wsl[@]}") +"${scan[@]}" +status=$? +"${scan_frozen[@]}" +frozen_status=$? +"${scan_frozen_wsl[@]}" +frozen_wsl_status=$? +statuses=("$status" "$frozen_status" "$frozen_wsl_status") +printf 'OSV primary exit codes: ordinary=%s frozen-macos=%s frozen-wsl=%s\n' "$status" "$frozen_status" "$frozen_wsl_status" +if [ "$WRITE_SARIF" = true ]; then + "${scan[@]}" --format sarif --output-file "$RUNNER_TEMP/osv-scanner/osv-scanner.sarif" + sarif_status=$? + "${scan_frozen[@]}" --format sarif --output-file "$RUNNER_TEMP/osv-scanner/osv-scanner-frozen-macos.sarif" + frozen_sarif_status=$? + "${scan_frozen_wsl[@]}" --format sarif --output-file "$RUNNER_TEMP/osv-scanner/osv-scanner-frozen-wsl-retrieval.sarif" + frozen_wsl_sarif_status=$? + statuses+=("$sarif_status" "$frozen_sarif_status" "$frozen_wsl_sarif_status") + printf 'OSV SARIF exit codes: ordinary=%s frozen-macos=%s frozen-wsl=%s\n' "$sarif_status" "$frozen_sarif_status" "$frozen_wsl_sarif_status" + # 0: no vulnerabilities; 1: vulnerabilities (already reported above); other codes are scanner errors. +fi +# Preserve the worst status from every primary and SARIF invocation, including findings and scanner errors. +for code in "${statuses[@]}"; do + if [ "$code" -gt "$status" ]; then + status=$code + fi +done +exit "$status" diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/controls.json b/evidence/artifacts/wsl-retrieval-retirement-20261003/controls.json new file mode 100644 index 000000000..fe3d3da77 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/controls.json @@ -0,0 +1,341 @@ +{ + "schema_version": 1, + "evidence_class": "native_cli_integration_controls", + "source_base_revision": "56473e4b840f0e6940c031801d866e7e9bf29baf", + "input_guard": { + "bytes": 283, + "sha256": "69ed6c37f8517e712ce64e1550c363926bdf25e42d8bfb30efa02b71197f3485" + }, + "input_lock": { + "bytes": 82463, + "sha256": "5c51ee65cc477f2c1488a38ff5cad1c0a737f81a5b61bbd70d5edc4d15bfc3bb" + }, + "native_versions": { + "npm": "11.19.0", + "node": "v24.21.0", + "osv": "2.6.0" + }, + "npm_controls": [ + { + "case": "install", + "native_exit": 1, + "runtime": "npm11.19.0/node24.21.0", + "output_files": [ + { + "path": "npm-install.stdout.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "npm-install.stderr.txt", + "bytes": 637, + "sha256": "813228f309860963e373f19fe5dce00ebee8aa83e7d406ccd0e63bd233524e94" + } + ], + "command_observation": { + "path": "npm-install.native-command.txt", + "sha256": "591050f725b226fceacb6dd41a607fe2465def6c0432edc08051e92e1894229f", + "bytes": 272, + "basis": "Selected original npm debug-log lines; prefixes/paths sanitized, line numbers retained." + }, + "post_observation": { + "node_modules_present": false, + "manifest_matches_input": true, + "lock_matches_input": true + } + }, + { + "case": "ci", + "native_exit": 1, + "runtime": "npm11.19.0/node24.21.0", + "output_files": [ + { + "path": "npm-ci.stdout.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "npm-ci.stderr.txt", + "bytes": 632, + "sha256": "12dd3fb22d309e147f2b5e5fcb06040c645487b6996c846925bda70b52247852" + } + ], + "command_observation": { + "path": "npm-ci.native-command.txt", + "sha256": "78e902a357299fdc7b2a79b7512084ca83fc2c112cd69e7e4e2da1a85513cb7e", + "bytes": 262, + "basis": "Selected original npm debug-log lines; prefixes/paths sanitized, line numbers retained." + }, + "post_observation": { + "node_modules_present": false, + "manifest_matches_input": true, + "lock_matches_input": true + } + }, + { + "case": "install-ignore", + "native_exit": 1, + "runtime": "npm11.19.0/node24.21.0", + "output_files": [ + { + "path": "npm-install-ignore.stdout.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "npm-install-ignore.stderr.txt", + "bytes": 644, + "sha256": "3df09a18c83259eb852943e8801ded30e3d070adb19f96df0715e6ca03160682" + } + ], + "command_observation": { + "path": "npm-install-ignore.native-command.txt", + "sha256": "351569d470470479f317374dc31ce440bf20ec97f80a8e4db9465fc0fe01891c", + "bytes": 298, + "basis": "Selected original npm debug-log lines; prefixes/paths sanitized, line numbers retained." + }, + "post_observation": { + "node_modules_present": false, + "manifest_matches_input": true, + "lock_matches_input": true + } + }, + { + "case": "ci-ignore", + "native_exit": 1, + "runtime": "npm11.19.0/node24.21.0", + "output_files": [ + { + "path": "npm-ci-ignore.stdout.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "npm-ci-ignore.stderr.txt", + "bytes": 639, + "sha256": "0e2a6f0dfbc8166331bd4e4e62f03dd76aae4e938dc3916e4503b362279e9c24" + } + ], + "command_observation": { + "path": "npm-ci-ignore.native-command.txt", + "sha256": "1654f7d1ed9773f268e4f984aca1cd950b378690a1bdc20777b217a452754841", + "bytes": 288, + "basis": "Selected original npm debug-log lines; prefixes/paths sanitized, line numbers retained." + }, + "post_observation": { + "node_modules_present": false, + "manifest_matches_input": true, + "lock_matches_input": true + } + }, + { + "case": "install-prefix", + "native_exit": 1, + "runtime": "npm11.19.0/node24.21.0", + "output_files": [ + { + "path": "npm-install-prefix.stdout.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "npm-install-prefix.stderr.txt", + "bytes": 644, + "sha256": "db82ddd782c6966f9546348d032ae3c5311ee8e53f15f04660673c14f4c2f5e9" + } + ], + "command_observation": { + "path": "npm-install-prefix.native-command.txt", + "sha256": "a3f6d02228d6d8f7b705410c0c7f16ee3a67dbbd83e74bf44cc63c1e7151b5e8", + "bytes": 309, + "basis": "Selected original npm debug-log lines; prefixes/paths sanitized, line numbers retained." + }, + "post_observation": { + "node_modules_present": false, + "manifest_matches_input": true, + "lock_matches_input": true + } + }, + { + "case": "ci-prefix", + "native_exit": 1, + "runtime": "npm11.19.0/node24.21.0", + "output_files": [ + { + "path": "npm-ci-prefix.stdout.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "npm-ci-prefix.stderr.txt", + "bytes": 639, + "sha256": "417b69b6d42f8960cd37baaaebeb48c28fececb9c72d289aad15cb20a192b0a3" + } + ], + "command_observation": { + "path": "npm-ci-prefix.native-command.txt", + "sha256": "00613e888df6ccca4c05e3ddfedbf070b1b70a1ced962efe3f3469fb8356809a", + "bytes": 299, + "basis": "Selected original npm debug-log lines; prefixes/paths sanitized, line numbers retained." + }, + "post_observation": { + "node_modules_present": false, + "manifest_matches_input": true, + "lock_matches_input": true + } + }, + { + "case": "install-prefix-ignore", + "native_exit": 1, + "runtime": "npm11.19.0/node24.21.0", + "output_files": [ + { + "path": "npm-install-prefix-ignore.stdout.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "npm-install-prefix-ignore.stderr.txt", + "bytes": 651, + "sha256": "46b7816e3ccca428816bb99ab777f86b098c11359890d4d89c148276050b7ed5" + } + ], + "command_observation": { + "path": "npm-install-prefix-ignore.native-command.txt", + "sha256": "714336d9bd268dec7e70329219ee4483ebbf7272cfffaacba20e03083fc366a5", + "bytes": 335, + "basis": "Selected original npm debug-log lines; prefixes/paths sanitized, line numbers retained." + }, + "post_observation": { + "node_modules_present": false, + "manifest_matches_input": true, + "lock_matches_input": true + } + }, + { + "case": "ci-prefix-ignore", + "native_exit": 1, + "runtime": "npm11.19.0/node24.21.0", + "output_files": [ + { + "path": "npm-ci-prefix-ignore.stdout.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "npm-ci-prefix-ignore.stderr.txt", + "bytes": 646, + "sha256": "4265db3c389d726228062ba7d1a91d10462aca40cf8d71e34a4345ece225d0ab" + } + ], + "command_observation": { + "path": "npm-ci-prefix-ignore.native-command.txt", + "sha256": "13cddf30b430b728b0eccd38a08b116ec87941bf851d7b84e3d087ab540b2a05", + "bytes": 325, + "basis": "Selected original npm debug-log lines; prefixes/paths sanitized, line numbers retained." + }, + "post_observation": { + "node_modules_present": false, + "manifest_matches_input": true, + "lock_matches_input": true + } + } + ], + "scanner_controls": [ + { + "case": "ordinary-active", + "exit_code": 1, + "advisory": "GHSA-vfj7-8cjw-p6xm", + "output_files": [ + { + "path": "ordinary-active.stdout.json", + "bytes": 4238, + "sha256": "977a6e94151eadfc050710483f14683896a8b072585076f1a34844fd71845cfe" + }, + { + "path": "ordinary-active.stderr.txt", + "bytes": 382, + "sha256": "910710222f50d1e2e0436a5ae85c7eabaa69cfcac730e1047454f4e0ad10a971" + } + ] + }, + { + "case": "prospective-archive", + "attempt": 1, + "exit_code": 127, + "verdict": "Malformed quoted-date input; not vulnerability/expiry acceptance.", + "output_files": [ + { + "path": "prospective-archive.stdout.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "prospective-archive.stderr.txt", + "bytes": 294, + "sha256": "51d1d7bf8dd549a7e1e7fec51000cb7010e626455904fc852033c6d05f64d26f" + } + ] + }, + { + "case": "expired-archive", + "attempt": 1, + "exit_code": 127, + "verdict": "Malformed quoted-date input; not vulnerability/expiry acceptance.", + "output_files": [ + { + "path": "expired-archive.stdout.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "expired-archive.stderr.txt", + "bytes": 294, + "sha256": "d3f5e81d6d698b0ae93a28c9bfeec7098ed95ad8d719e7da64dc2c8afec42b2e" + } + ] + }, + { + "case": "prospective-archive-attempt2", + "exit_code": 0, + "output_files": [ + { + "path": "prospective-archive-attempt2.stdout.json", + "bytes": 124, + "sha256": "54345d5194d238b6017d0f3181f392109ce35023cc6b4bdfa8c134943dcb578e" + }, + { + "path": "prospective-archive-attempt2.stderr.txt", + "bytes": 968, + "sha256": "500d6eb187a05311b37d0cb6d03ea2752b365cf869d44fb98bfc37930d876b11" + } + ] + }, + { + "case": "expired-archive-attempt2", + "exit_code": 1, + "advisory": "GHSA-vfj7-8cjw-p6xm", + "output_files": [ + { + "path": "expired-archive-attempt2.stdout.json", + "bytes": 4238, + "sha256": "977a6e94151eadfc050710483f14683896a8b072585076f1a34844fd71845cfe" + }, + { + "path": "expired-archive-attempt2.stderr.txt", + "bytes": 319, + "sha256": "fbda52df16997854cca318d7b1c11500610161cfc6c5c8229ed17a157e7d492e" + } + ] + } + ], + "output_policy": "Published stdout/stderr retain original text with task-owned host paths replaced by role markers. Every published digest hashes only the published bytes; private originals remain outside the checkout. Command observations are selected original npm debug-log lines, not reconstructed executions.", + "environment_contract": "Owned empty HOME, separate owned user/global npm configs, each case own cache; cleared inherited environment; npm offline=true/audit=false/fund=false/update_notifier=false. Prefix cases run outside fixture cwd; no manual native-account read/copy.", + "limits": [ + "Prospective private config controls only; repository exception/workflow not yet adopted.", + "Eight install/ci/ignore-scripts/prefix controls each EBADDEVENGINES, no node_modules, guard/lock hashes unchanged.", + "Own HOME, separate owned user/global npm configs and cleared inherited environment; no manual credential-store read/copy. No exhaustive filesystem audit claimed.", + "Explicit --force, other package managers and restored historical source remain outside supported-entry guard.", + "Active QMD vulnerability remains unresolved; native CLI integration controls are not upstream tests or whole-host qualification." + ] +} diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/expired-archive-attempt2.stderr.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/expired-archive-attempt2.stderr.txt new file mode 100644 index 000000000..2c6533d5e --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/expired-archive-attempt2.stderr.txt @@ -0,0 +1,5 @@ +Starting filesystem walk for root: / +Scanned /ordinary-active/package-lock.json file and found 170 packages +End status: 0 dirs visited, 1 inodes visited, 1 Extract calls, 5.663705ms elapsed, 5.663785ms wall time +/candidate-archive-expired.toml has unused ignores: + - GHSA-vfj7-8cjw-p6xm diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/expired-archive-attempt2.stdout.json b/evidence/artifacts/wsl-retrieval-retirement-20261003/expired-archive-attempt2.stdout.json new file mode 100644 index 000000000..99507be48 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/expired-archive-attempt2.stdout.json @@ -0,0 +1,124 @@ +{ + "results": [ + { + "source": { + "path": "/ordinary-active/package-lock.json", + "type": "lockfile" + }, + "packages": [ + { + "package": { + "name": "braces", + "version": "3.0.3", + "ecosystem": "npm" + }, + "groups": [ + { + "ids": [ + "GHSA-vfj7-8cjw-p6xm" + ], + "aliases": [ + "CVE-2026-93687", + "GHSA-vfj7-8cjw-p6xm" + ], + "max_severity": "8.7" + } + ], + "vulnerabilities": [ + { + "affected": [ + { + "database_specific": { + "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/09/GHSA-vfj7-8cjw-p6xm/GHSA-vfj7-8cjw-p6xm.json" + }, + "package": { + "ecosystem": "npm", + "name": "braces", + "purl": "pkg:npm/braces" + }, + "ranges": [ + { + "events": [ + { + "introduced": "0" + }, + { + "last_affected": "3.0.3" + } + ], + "type": "SEMVER" + } + ] + } + ], + "aliases": [ + "CVE-2026-93687" + ], + "database_specific": { + "cwe_ids": [ + "CWE-674" + ], + "github_reviewed": true, + "github_reviewed_at": "2026-10-02T22:36:33Z", + "nvd_published_at": "2026-09-18T16:17:15Z", + "severity": "HIGH" + }, + "details": "braces through 3.0.3 contains a stack overflow vulnerability in the recursive AST walkers that lack depth guards. Attackers can supply deeply nested brace patterns under the character limit to exhaust the call stack and terminate the Node.js process with an uncaught RangeError.", + "id": "GHSA-vfj7-8cjw-p6xm", + "modified": "2026-10-02T22:45:04.328737432Z", + "published": "2026-09-18T18:31:41Z", + "references": [ + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-93687" + }, + { + "type": "WEB", + "url": "https://github.com/micromatch/braces/issues/70" + }, + { + "type": "PACKAGE", + "url": "https://github.com/micromatch/braces" + }, + { + "type": "WEB", + "url": "https://github.com/micromatch/braces/blob/3.0.3/lib/compile.js#L49-L53" + }, + { + "type": "WEB", + "url": "https://github.com/micromatch/braces/blob/3.0.3/lib/expand.js#L102-L105" + }, + { + "type": "WEB", + "url": "https://github.com/micromatch/braces/blob/3.0.3/lib/parse.js#L38-L40" + }, + { + "type": "WEB", + "url": "https://www.vulncheck.com/advisories/braces-through-3.0.3-stack-overflow-via-deeply-nested-patterns" + } + ], + "schema_version": "1.9.0", + "severity": [ + { + "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H", + "type": "CVSS_V3" + }, + { + "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N", + "type": "CVSS_V4" + } + ], + "summary": "braces vulnerable to stack-exhaustion denial of service through deeply nested patterns" + } + ] + } + ] + } + ], + "experimental_config": { + "licenses": { + "summary": false, + "allowlist": null + } + } +} diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/expired-archive.stderr.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/expired-archive.stderr.txt new file mode 100644 index 000000000..29ec8e73e --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/expired-archive.stderr.txt @@ -0,0 +1,2 @@ +Failed to read config file: toml: line 4 (last key "IgnoredVulns.ignoreUntil"): parsing time "2026-10-01" as "2006-01-02T15:04:05Z07:00": cannot parse "" as "T" +toml: line 4 (last key "IgnoredVulns.ignoreUntil"): parsing time "2026-10-01" as "2006-01-02T15:04:05Z07:00": cannot parse "" as "T" diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/expired-archive.stdout.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/expired-archive.stdout.txt new file mode 100644 index 000000000..e69de29bb diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/final-native-scan-record.json b/evidence/artifacts/wsl-retrieval-retirement-20261003/final-native-scan-record.json new file mode 100644 index 000000000..bf2a937fd --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/final-native-scan-record.json @@ -0,0 +1,306 @@ +{ + "schema_version": 1, + "evidence_class": "native_cli_integration_controls", + "source_base_revision": "56473e4b840f0e6940c031801d866e7e9bf29baf", + "workflow_path": ".github/workflows/security-scan.yml", + "workflow_sha256": "1744bb958b1455bbd4462c6dd61ab2a08ce1e15eeca2522611826c146bbadfc2", + "exact_extracted_script_sha256": "f55f319bcfd2a1bf5fbbe19c1cd0b2155b75a8086b34586465b67c1db0d06625", + "observed_native_exit": 0, + "native_primary_statuses": { + "ordinary": 0, + "frozen-macos": 0, + "frozen-wsl": 0 + }, + "native_sarif_statuses": { + "ordinary": 0, + "frozen-macos": 0, + "frozen-wsl": 0 + }, + "native_guard_tests": { + "tests": 20, + "exit": 0 + }, + "authored_integrated_regression_tests": { + "tests": 46, + "exit": 0 + }, + "partitions": { + ".github/osv-scanner.toml": [ + { + "path": ".github/requirements-ci.txt", + "parser": "requirements.txt" + }, + { + "path": "blueprints/us-equities/sim-crosscheck-hftbacktest/requirements.lock", + "parser": "requirements.txt" + }, + { + "path": "adoption/sdk/accepted-constraints.txt", + "parser": "requirements.txt" + }, + { + "path": "adoption/sdk/requirements-linux-x86_64-py313.lock", + "parser": "requirements.txt" + }, + { + "path": "blueprints/convergence-practice/application-delivery/pnpm-lock.yaml", + "parser": null + }, + { + "path": "blueprints/convergence-practice/application-delivery/uv.lock", + "parser": null + }, + { + "path": "blueprints/memory-stack/native-qualification/uv.lock", + "parser": null + }, + { + "path": "blueprints/us-equities/adaptive-paper/requirements.txt", + "parser": null + }, + { + "path": "blueprints/us-equities/catalyst-provenance/requirements.lock", + "parser": "requirements.txt" + }, + { + "path": "blueprints/us-equities/data/requirements.in", + "parser": "requirements.txt" + }, + { + "path": "blueprints/us-equities/data/requirements.lock", + "parser": "requirements.txt" + }, + { + "path": "blueprints/runtime-workers/openhands/requirements.lock", + "parser": "requirements.txt" + }, + { + "path": "blueprints/runtime-workers/openhands/build-requirements.lock", + "parser": "requirements.txt" + }, + { + "path": "blueprints/us-equities/engine/patches/alpaca.packages.lock.json", + "parser": "packages.lock.json" + }, + { + "path": "blueprints/us-equities/engine/patches/lean-locks/Algorithm.CSharp/packages.lock.json", + "parser": null + }, + { + "path": "blueprints/us-equities/engine/patches/lean-locks/Algorithm.Framework/packages.lock.json", + "parser": null + }, + { + "path": "blueprints/us-equities/engine/patches/lean-locks/Algorithm/packages.lock.json", + "parser": null + }, + { + "path": "blueprints/us-equities/engine/patches/lean-locks/AlgorithmFactory/packages.lock.json", + "parser": null + }, + { + "path": "blueprints/us-equities/engine/patches/lean-locks/Api/packages.lock.json", + "parser": null + }, + { + "path": "blueprints/us-equities/engine/patches/lean-locks/Brokerages/packages.lock.json", + "parser": null + }, + { + "path": "blueprints/us-equities/engine/patches/lean-locks/Common/packages.lock.json", + "parser": null + }, + { + "path": "blueprints/us-equities/engine/patches/lean-locks/Compression/packages.lock.json", + "parser": null + }, + { + "path": "blueprints/us-equities/engine/patches/lean-locks/Configuration/packages.lock.json", + "parser": null + }, + { + "path": "blueprints/us-equities/engine/patches/lean-locks/DownloaderDataProvider/packages.lock.json", + "parser": null + }, + { + "path": "blueprints/us-equities/engine/patches/lean-locks/Engine/packages.lock.json", + "parser": null + }, + { + "path": "blueprints/us-equities/engine/patches/lean-locks/Indicators/packages.lock.json", + "parser": null + }, + { + "path": "blueprints/us-equities/engine/patches/lean-locks/Launcher/packages.lock.json", + "parser": null + }, + { + "path": "blueprints/us-equities/engine/patches/lean-locks/Logging/packages.lock.json", + "parser": null + }, + { + "path": "blueprints/us-equities/engine/patches/lean-locks/Messaging/packages.lock.json", + "parser": null + }, + { + "path": "blueprints/us-equities/engine/patches/lean-locks/Optimizer.Launcher/packages.lock.json", + "parser": null + }, + { + "path": "blueprints/us-equities/engine/patches/lean-locks/Optimizer/packages.lock.json", + "parser": null + }, + { + "path": "blueprints/us-equities/engine/patches/lean-locks/Queues/packages.lock.json", + "parser": null + }, + { + "path": "blueprints/us-equities/engine/patches/lean-locks/Report/packages.lock.json", + "parser": null + }, + { + "path": "blueprints/us-equities/engine/patches/lean-locks/Research/packages.lock.json", + "parser": null + }, + { + "path": "blueprints/us-equities/engine/patches/lean-locks/Tests/packages.lock.json", + "parser": null + }, + { + "path": "blueprints/us-equities/engine/patches/lean-locks/ToolBox/packages.lock.json", + "parser": null + }, + { + "path": "blueprints/us-equities/research-evaluation/requirements.in", + "parser": "requirements.txt" + }, + { + "path": "blueprints/us-equities/research-evaluation/requirements.lock", + "parser": "requirements.txt" + }, + { + "path": "blueprints/us-equities/workers/requirements.txt", + "parser": null + }, + { + "path": "tools/mlx-smoke/requirements.in", + "parser": "requirements.txt" + }, + { + "path": "tools/mlx-smoke/requirements.lock.txt", + "parser": "requirements.txt" + }, + { + "path": "blueprints/gap-wave2-20260923/us-equities__portfolio-risk/requirements-libs.lock", + "parser": "requirements.txt" + }, + { + "path": "blueprints/us-equities/mover-v3/data-tools/requirements.lock", + "parser": "requirements.txt" + }, + { + "path": "blueprints/us-equities/mover-v3/study/runtime-requirements.txt", + "parser": "requirements.txt" + }, + { + "path": "evidence/artifacts/macos-syn-e2e-20260924/requirements-macos-arm64-py312.lock", + "parser": "requirements.txt" + }, + { + "path": "blueprints/us-equities/engine-trials/spy-one-zero-20260926/ml4t-backtest/lockcheck/ml4t.lock", + "parser": "requirements.txt" + }, + { + "path": "blueprints/us-equities/engine-trials/spy-one-zero-20260926/lumibot/lockcheck/lumibot.lock", + "parser": "requirements.txt" + }, + { + "path": "blueprints/us-equities/engine-trials/spy-one-zero-20260926/lumibot/lockcheck/ibapi-build/build.lock", + "parser": "requirements.txt" + } + ], + ".github/osv-scanner-frozen-wsl-retrieval.toml": [ + { + "path": "blueprints/convergence-practice/wsl-retrieval/package-lock.json", + "parser": null + } + ], + ".github/osv-scanner-frozen-macos.toml": [ + { + "path": "evidence/artifacts/macos-application-20260924/variant/pnpm-lock.yaml", + "parser": null + } + ] + }, + "expired_final_config_control": { + "native_exit": 1, + "advisory": "GHSA-vfj7-8cjw-p6xm", + "config_sha256": "a820c6eff5eed054a1d1b407e7fb186b1501bbf98b246fb0bb80fc846a5718da", + "only_changed_semantic_field": "IgnoredVulns[0].ignoreUntil from2026-10-31 to2026-10-01" + }, + "unchanged_active_control": "Existing ordinary-active copy scan1 reports GHSA; exact lock input and unchanged ordinary config are reused, not a new execution. Dedicated config applies only one inventory input.", + "environment_contract": "env-i,PATH/usr/bin:/bin,ownedemptyHOME,ownedRUNNER_TEMP,WRITE_SARIF=true,bash--noprofile--norc; no manual credentials, global install or host service effects. Native script is exact extracted current workflow step, unchanged CLI invocations; generated artifact, not separately authored orchestration.", + "artifacts": [ + { + "path": "final-scan-stdout.txt", + "bytes": 18417, + "sha256": "15bd287f37de30aef9dd02294dcb3445160f288eac9572d809a506123e7d678a" + }, + { + "path": "final-scan-stderr.txt", + "bytes": 18363, + "sha256": "9063e604c63e35a3888e392a3cacc577bff8e7ad2ba65c221eabfdb5117f7a00" + }, + { + "path": "final-scan-workflow-scan.sh", + "bytes": 4304, + "sha256": "f55f319bcfd2a1bf5fbbe19c1cd0b2155b75a8086b34586465b67c1db0d06625" + }, + { + "path": "final-scan-final-config-expired.toml", + "bytes": 2330, + "sha256": "a820c6eff5eed054a1d1b407e7fb186b1501bbf98b246fb0bb80fc846a5718da" + }, + { + "path": "final-scan-final-expired.stdout.json", + "bytes": 4268, + "sha256": "12e0886391e92655365a6e1dd67c99739fe4d1fa177207e98ef5e2a7adfabb12" + }, + { + "path": "final-scan-final-expired.stderr.txt", + "bytes": 347, + "sha256": "e45f599be0d3d0dd13a76c710466aeb34a308e2ba634286b1530b4cb50cdf821" + }, + { + "path": "osv-scanner-frozen-macos.sarif", + "bytes": 1168, + "sha256": "4568c81b00798436c2ec0a6ce67d41500aa483516d6b354f00bd1885c50b5955" + }, + { + "path": "osv-scanner.sarif", + "bytes": 1168, + "sha256": "4568c81b00798436c2ec0a6ce67d41500aa483516d6b354f00bd1885c50b5955" + }, + { + "path": "osv-scanner-frozen-wsl-retrieval.sarif", + "bytes": 1168, + "sha256": "4568c81b00798436c2ec0a6ce67d41500aa483516d6b354f00bd1885c50b5955" + }, + { + "path": "integrated-osv-tests.stdout.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "integrated-osv-tests.stderr.txt", + "bytes": 7766, + "sha256": "266207212c8b17dfdd323d0ab321fcefd27ac03d5d70c6655ce1ceeccab80488" + } + ], + "claim_limits": [ + "Native workflow/CLI integration, not upstream suite or target-host/provider/GPU qualification.", + "48/1/1 assignment and preserved parsers bind inventory and exact native script; no kernel execution tracing. Synthetic scope-refusal/status tests remain separate authored integration checks.", + "Archive native0 is a narrow dated policy disposition, not a dependency patch or activeQMD exception. Final source review/currenthead/sharedACK/CI remain necessary.", + "Output paths are role markers; digests bind published bytes only; private original native outputs remain outsidecheckout." + ] +} diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/final-scan-final-config-expired.toml b/evidence/artifacts/wsl-retrieval-retirement-20261003/final-scan-final-config-expired.toml new file mode 100644 index 000000000..4a66be13f --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/final-scan-final-config-expired.toml @@ -0,0 +1,12 @@ +# Dedicated OSV-Scanner v2.6.0 configuration for the retired historical WSL retrieval lock only. +# The explicit --config applies to every input of its invocation, not to a path encoded in this file: +# https://github.com/google/osv-scanner/blob/v2.6.0/docs/configuration.md +# https://github.com/google/osv-scanner/blob/v2.6.0/internal/config/manager.go (Manager.Get). +# The workflow's three disjoint input lists and preflight policy tests enforce the exact caller scope, +# reviewed digest, evidence, retirement guard and expiry. No ordinary or active QMD input receives this config. +# Its native bare-date syntax matches the unchanged main564 macOS archive pattern and the retained root control. + +[[IgnoredVulns]] +id = "GHSA-vfj7-8cjw-p6xm" +ignoreUntil = 2026-10-01 +reason = "2026-10-03 bounded retirement review: this config is assigned only to blueprints/convergence-practice/wsl-retrieval/package-lock.json at SHA256 5c51ee65cc477f2c1488a38ff5cad1c0a737f81a5b61bbd70d5edc4d15bfc3bb. It preserves the original lock as historical evidence; braces 3.0.3 remains affected by GHSA-vfj7-8cjw-p6xm/CVE-2026-93687. Both old source/QMD runner modes stop before subprocess or output creation, and a dependency-free private manifest uses npm 11.19.0 devEngines.runtime.name=retired-wsl-retrieval/onFail=error to reject supported install/ci entry points. Original manifest/recording-aid bytes are archived and original receipts, failure facts and prior runner archives remain unchanged. This is a supported-entry-point guard, not an installation sandbox: explicit --force, other package managers and restored historical files are outside its claim. Source disposition and exact artifact bindings: blueprints/convergence-practice/wsl-retrieval/retirement-assessment.json. Independent native controls and retained failures: evidence/receipts/wsl-retrieval-retirement-20261003.json. Owner handoff: https://github.com/seathatflowsinourveins/native-agent-stack/pull/608#issuecomment-5963764481. The workflow and inventory tests constrain this one input; OSV itself does not constrain an explicit config to that path. Active QMD remains separately unresolved and ordinary/macOS configs carry no exception for this advisory. This exception expires at the native TOML date 2026-10-31; retirement does not patch a dependency or qualify active QMD." diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/final-scan-final-expired.stderr.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/final-scan-final-expired.stderr.txt new file mode 100644 index 000000000..b551205e9 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/final-scan-final-expired.stderr.txt @@ -0,0 +1,5 @@ +Starting filesystem walk for root: / +Scanned /blueprints/convergence-practice/wsl-retrieval/package-lock.json file and found 170 packages +End status: 0 dirs visited, 1 inodes visited, 1 Extract calls, 5.417321ms elapsed, 5.417381ms wall time +/final-config-expired.toml has unused ignores: + - GHSA-vfj7-8cjw-p6xm diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/final-scan-final-expired.stdout.json b/evidence/artifacts/wsl-retrieval-retirement-20261003/final-scan-final-expired.stdout.json new file mode 100644 index 000000000..a86c97190 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/final-scan-final-expired.stdout.json @@ -0,0 +1,124 @@ +{ + "results": [ + { + "source": { + "path": "/blueprints/convergence-practice/wsl-retrieval/package-lock.json", + "type": "lockfile" + }, + "packages": [ + { + "package": { + "name": "braces", + "version": "3.0.3", + "ecosystem": "npm" + }, + "groups": [ + { + "ids": [ + "GHSA-vfj7-8cjw-p6xm" + ], + "aliases": [ + "CVE-2026-93687", + "GHSA-vfj7-8cjw-p6xm" + ], + "max_severity": "8.7" + } + ], + "vulnerabilities": [ + { + "affected": [ + { + "database_specific": { + "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/09/GHSA-vfj7-8cjw-p6xm/GHSA-vfj7-8cjw-p6xm.json" + }, + "package": { + "ecosystem": "npm", + "name": "braces", + "purl": "pkg:npm/braces" + }, + "ranges": [ + { + "events": [ + { + "introduced": "0" + }, + { + "last_affected": "3.0.3" + } + ], + "type": "SEMVER" + } + ] + } + ], + "aliases": [ + "CVE-2026-93687" + ], + "database_specific": { + "cwe_ids": [ + "CWE-674" + ], + "github_reviewed": true, + "github_reviewed_at": "2026-10-02T22:36:33Z", + "nvd_published_at": "2026-09-18T16:17:15Z", + "severity": "HIGH" + }, + "details": "braces through 3.0.3 contains a stack overflow vulnerability in the recursive AST walkers that lack depth guards. Attackers can supply deeply nested brace patterns under the character limit to exhaust the call stack and terminate the Node.js process with an uncaught RangeError.", + "id": "GHSA-vfj7-8cjw-p6xm", + "modified": "2026-10-02T22:45:04.328737432Z", + "published": "2026-09-18T18:31:41Z", + "references": [ + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-93687" + }, + { + "type": "WEB", + "url": "https://github.com/micromatch/braces/issues/70" + }, + { + "type": "PACKAGE", + "url": "https://github.com/micromatch/braces" + }, + { + "type": "WEB", + "url": "https://github.com/micromatch/braces/blob/3.0.3/lib/compile.js#L49-L53" + }, + { + "type": "WEB", + "url": "https://github.com/micromatch/braces/blob/3.0.3/lib/expand.js#L102-L105" + }, + { + "type": "WEB", + "url": "https://github.com/micromatch/braces/blob/3.0.3/lib/parse.js#L38-L40" + }, + { + "type": "WEB", + "url": "https://www.vulncheck.com/advisories/braces-through-3.0.3-stack-overflow-via-deeply-nested-patterns" + } + ], + "schema_version": "1.9.0", + "severity": [ + { + "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H", + "type": "CVSS_V3" + }, + { + "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N", + "type": "CVSS_V4" + } + ], + "summary": "braces vulnerable to stack-exhaustion denial of service through deeply nested patterns" + } + ] + } + ] + } + ], + "experimental_config": { + "licenses": { + "summary": false, + "allowlist": null + } + } +} diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/final-scan-stderr.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/final-scan-stderr.txt new file mode 100644 index 000000000..8cc3b30a6 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/final-scan-stderr.txt @@ -0,0 +1,73 @@ +.................... +---------------------------------------------------------------------- +Ran 20 tests in 0.537s + +OK +Starting filesystem walk for root: / +Scanned /.github/requirements-ci.txt file and found 1 package +Scanned /blueprints/us-equities/sim-crosscheck-hftbacktest/requirements.lock file and found 43 packages +Scanned /adoption/sdk/accepted-constraints.txt file and found 36 packages +Scanned /adoption/sdk/requirements-linux-x86_64-py313.lock file and found 36 packages +Scanned /blueprints/convergence-practice/application-delivery/pnpm-lock.yaml file and found 109 packages +Scanned /blueprints/convergence-practice/application-delivery/uv.lock file and found 31 packages +Scanned /blueprints/memory-stack/native-qualification/uv.lock file and found 86 packages +Scanned /blueprints/us-equities/adaptive-paper/requirements.txt file and found 2 packages +Scanned /blueprints/us-equities/catalyst-provenance/requirements.lock file and found 42 packages +Scanned /blueprints/us-equities/data/requirements.in file and found 5 packages +Scanned /blueprints/us-equities/data/requirements.lock file and found 21 packages +Scanned /blueprints/runtime-workers/openhands/requirements.lock file and found 334 packages +Scanned /blueprints/runtime-workers/openhands/build-requirements.lock file and found 3 packages +Scanned /blueprints/us-equities/engine/patches/alpaca.packages.lock.json file and found 45 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Algorithm.CSharp/packages.lock.json file and found 30 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Algorithm.Framework/packages.lock.json file and found 26 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Algorithm/packages.lock.json file and found 22 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/AlgorithmFactory/packages.lock.json file and found 23 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Api/packages.lock.json file and found 22 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Brokerages/packages.lock.json file and found 24 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Common/packages.lock.json file and found 20 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Compression/packages.lock.json file and found 6 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Configuration/packages.lock.json file and found 5 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/DownloaderDataProvider/packages.lock.json file and found 37 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Engine/packages.lock.json file and found 36 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Indicators/packages.lock.json file and found 21 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Launcher/packages.lock.json file and found 81 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Logging/packages.lock.json file and found 1 package +Scanned /blueprints/us-equities/engine/patches/lean-locks/Messaging/packages.lock.json file and found 28 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Optimizer.Launcher/packages.lock.json file and found 22 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Optimizer/packages.lock.json file and found 21 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Queues/packages.lock.json file and found 25 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Report/packages.lock.json file and found 48 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Research/packages.lock.json file and found 68 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Tests/packages.lock.json file and found 97 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/ToolBox/packages.lock.json file and found 37 packages +Scanned /blueprints/us-equities/research-evaluation/requirements.in file and found 2 packages +Scanned /blueprints/us-equities/research-evaluation/requirements.lock file and found 19 packages +Scanned /blueprints/us-equities/workers/requirements.txt file and found 5 packages +Scanned /tools/mlx-smoke/requirements.in file and found 3 packages +Scanned /tools/mlx-smoke/requirements.lock.txt file and found 34 packages +Scanned /blueprints/gap-wave2-20260923/us-equities__portfolio-risk/requirements-libs.lock file and found 56 packages +Scanned /blueprints/us-equities/mover-v3/data-tools/requirements.lock file and found 9 packages +Scanned /blueprints/us-equities/mover-v3/study/runtime-requirements.txt file and found 10 packages +Scanned /evidence/artifacts/macos-syn-e2e-20260924/requirements-macos-arm64-py312.lock file and found 20 packages +Scanned /blueprints/us-equities/engine-trials/spy-one-zero-20260926/ml4t-backtest/lockcheck/ml4t.lock file and found 15 packages +Scanned /blueprints/us-equities/engine-trials/spy-one-zero-20260926/lumibot/lockcheck/lumibot.lock file and found 264 packages +Scanned /blueprints/us-equities/engine-trials/spy-one-zero-20260926/lumibot/lockcheck/ibapi-build/build.lock file and found 4 packages +End status: 0 dirs visited, 48 inodes visited, 48 Extract calls, 109.609855ms elapsed, 109.609905ms wall time +Filtered 175 local/unscannable package/s from the scan. +GHSA-hj66-6f7g-4r5v and 2 aliases have been filtered out because: oauthlib 0.6.1 through 3.3.1 (fixed in 4.0.0): RevocationEndpoint wraps its response in the request's callback parameter only when constructed with enable_jsonp=True (oauthlib/oauth2/rfc6749/endpoints/revocation.py:72-73 and 81-82 in 3.3.1); oauthlib's pre-configured servers never pass it. Two allowed locks pin 3.3.1: the OpenHands recipe lock and the frozen, evaluation-only Lumibot lock (run offline under bwrap). A text search of one hash-verified artifact per requirement of both locks found no enable_jsonp or create_revocation_response and no importer of oauthlib's endpoints, grant types or servers: oauthlib is imported only by requests-oauthlib 2.0.0 (client classes, token errors and helpers) and, in the Lumibot lock, kubernetes 36.0.3 (an exception class); every other RevocationEndpoint is Authlib's own class, and Authlib does not import oauthlib. Evidence: evidence/receipts/osv-oauthlib-pyjwt-reachability-20260929.json and, for the OpenHands lock at its relocked sha256, evidence/receipts/osv-openhands-pyjwt-relock-20260930.json. Expiry plan: the OpenHands lock was relocked onto PyJWT 2.14.0 on 2026-09-30 and still pins oauthlib 3.3.1; as of 2026-09-30, oauthlib 4.0.0 (PyPI, 2026-09-28T06:01Z) was inside the 7-day window the upstream workspace's exclude-newer applies (until 2026-10-05T06:01:19Z), so the relock onto it follows, tracked in #518 (owner: bc, session native-agent-stack-bc; earliest 2026-10-05T18:40:43Z, when PyJWT 2.15.1 has left its window too; due 2026-10-08), and in the same change deletes the OpenHands entry of IGNORE_ALLOWED_LOCKS, whose sha256 pin fails any change to that lock. The Lumibot lock stays frozen on 3.3.1, so on 2026-10-13 renew this ignore for that lock alone with a new dated reason, or delete that lock. +GHSA-xpv3-w29h-x7cv and 2 aliases have been filtered out because: oauthlib >= 3.0.0, < 4.0.0 (fixed in 4.0.0; the scanned locks pin 3.3.1): an oauthlib authorization server compares the PKCE code_verifier with == (code_challenge_method_s256 at oauthlib/oauth2/rfc6749/grant_types/authorization_code.py:49 and code_challenge_method_plain at :61, in 3.3.1), called only from AuthorizationCodeGrant.validate_token_request (line 506) when a token endpoint handles a request. Neither allowed lock (the OpenHands recipe lock and the frozen Lumibot lock) runs an oauthlib authorization server: in a text search of one hash-verified artifact per requirement of both locks, oauthlib is imported only by requests-oauthlib 2.0.0 (WebApplicationClient, LegacyApplicationClient, the oauth1 Client, token errors and helpers) and, in the Lumibot lock, kubernetes 36.0.3 (an exception class), and nothing imports oauthlib's grant types, endpoints or servers; the PKCE code in openhands-sdk, requests-oauthlib and google-auth-oauthlib only generates a verifier as a client. Evidence: evidence/receipts/osv-oauthlib-pyjwt-reachability-20260929.json and, for the OpenHands lock at its relocked sha256, evidence/receipts/osv-openhands-pyjwt-relock-20260930.json. Expiry plan: as for GHSA-hj66-6f7g-4r5v; the OpenHands relock onto oauthlib 4.0.0 removes that lock's pin, and on 2026-10-13 this ignore is renewed for the frozen Lumibot lock alone or that lock is deleted. +GHSA-8mgp-746c-j5xp and 2 aliases have been filtered out because: nltk through 3.10.3: TransitionParser.train/parse, AveragedPerceptron.save/load, PerceptronTagger.save_to_json and nltk.classify.maxent.save_maxent_params open caller-chosen model paths with the built-in open() instead of the pathsec helpers (the advisory's comparison case is the guarded PerceptronTagger.load_from_json, which in 3.10.3 still authorizes a caller-chosen private model directory), so untrusted input that chooses a model import or export path can read or write outside the allowed roots; pathsec enforcement is on by default (ENFORCE = True in nltk/pathsec.py of 3.10.3). No patched release as of 2026-09-27; the fixes are merged on develop (nltk #3757, #3759, #3813). Each lock allowed to pin nltk is named, with its reviewed sha256 and non-reachability evidence, in IGNORE_ALLOWED_LOCKS in tests/test_osv_lockfile_coverage.py. Lumibot lock: nltk 3.10.3 is a transitive pin (google-adk, llama-index-core) of the evaluation-only Lumibot 4.6.1 environment, run offline under bwrap, and no trial code calls these APIs (evidence: blueprints/us-equities/engine-trials/spy-one-zero-20260926/repository-checks.json). Expiry plan: relock every allowed lock onto the first nltk release that ships those fixes, then delete this ignore; delete the Lumibot lock unless a verdict has adopted Lumibot. +GHSA-hj66-6f7g-4r5v and 2 aliases have been filtered out because: oauthlib 0.6.1 through 3.3.1 (fixed in 4.0.0): RevocationEndpoint wraps its response in the request's callback parameter only when constructed with enable_jsonp=True (oauthlib/oauth2/rfc6749/endpoints/revocation.py:72-73 and 81-82 in 3.3.1); oauthlib's pre-configured servers never pass it. Two allowed locks pin 3.3.1: the OpenHands recipe lock and the frozen, evaluation-only Lumibot lock (run offline under bwrap). A text search of one hash-verified artifact per requirement of both locks found no enable_jsonp or create_revocation_response and no importer of oauthlib's endpoints, grant types or servers: oauthlib is imported only by requests-oauthlib 2.0.0 (client classes, token errors and helpers) and, in the Lumibot lock, kubernetes 36.0.3 (an exception class); every other RevocationEndpoint is Authlib's own class, and Authlib does not import oauthlib. Evidence: evidence/receipts/osv-oauthlib-pyjwt-reachability-20260929.json and, for the OpenHands lock at its relocked sha256, evidence/receipts/osv-openhands-pyjwt-relock-20260930.json. Expiry plan: the OpenHands lock was relocked onto PyJWT 2.14.0 on 2026-09-30 and still pins oauthlib 3.3.1; as of 2026-09-30, oauthlib 4.0.0 (PyPI, 2026-09-28T06:01Z) was inside the 7-day window the upstream workspace's exclude-newer applies (until 2026-10-05T06:01:19Z), so the relock onto it follows, tracked in #518 (owner: bc, session native-agent-stack-bc; earliest 2026-10-05T18:40:43Z, when PyJWT 2.15.1 has left its window too; due 2026-10-08), and in the same change deletes the OpenHands entry of IGNORE_ALLOWED_LOCKS, whose sha256 pin fails any change to that lock. The Lumibot lock stays frozen on 3.3.1, so on 2026-10-13 renew this ignore for that lock alone with a new dated reason, or delete that lock. +GHSA-xpv3-w29h-x7cv and 2 aliases have been filtered out because: oauthlib >= 3.0.0, < 4.0.0 (fixed in 4.0.0; the scanned locks pin 3.3.1): an oauthlib authorization server compares the PKCE code_verifier with == (code_challenge_method_s256 at oauthlib/oauth2/rfc6749/grant_types/authorization_code.py:49 and code_challenge_method_plain at :61, in 3.3.1), called only from AuthorizationCodeGrant.validate_token_request (line 506) when a token endpoint handles a request. Neither allowed lock (the OpenHands recipe lock and the frozen Lumibot lock) runs an oauthlib authorization server: in a text search of one hash-verified artifact per requirement of both locks, oauthlib is imported only by requests-oauthlib 2.0.0 (WebApplicationClient, LegacyApplicationClient, the oauth1 Client, token errors and helpers) and, in the Lumibot lock, kubernetes 36.0.3 (an exception class), and nothing imports oauthlib's grant types, endpoints or servers; the PKCE code in openhands-sdk, requests-oauthlib and google-auth-oauthlib only generates a verifier as a client. Evidence: evidence/receipts/osv-oauthlib-pyjwt-reachability-20260929.json and, for the OpenHands lock at its relocked sha256, evidence/receipts/osv-openhands-pyjwt-relock-20260930.json. Expiry plan: as for GHSA-hj66-6f7g-4r5v; the OpenHands relock onto oauthlib 4.0.0 removes that lock's pin, and on 2026-10-13 this ignore is renewed for the frozen Lumibot lock alone or that lock is deleted. +GHSA-h35f-9h28-mq5c and 3 aliases have been filtered out because: setuptools 80.10.2 in the same frozen evaluation-only lock, run offline under bwrap; the advisory needs an sdist build on macOS APFS/HFS+, while this environment was installed binary-only on Linux (--no-build) and its one source build used setuptools 84.0.0; relocking would change the recorded environment. At expiry, delete the Lumibot lock and these ignores unless Lumibot has been adopted by a verdict. Evidence: blueprints/us-equities/engine-trials/spy-one-zero-20260926/repository-checks.json +Filtered 11 vulnerabilities from output +Starting filesystem walk for root: / +Scanned /evidence/artifacts/macos-application-20260924/variant/pnpm-lock.yaml file and found 109 packages +End status: 0 dirs visited, 1 inodes visited, 1 Extract calls, 1.969291ms elapsed, 1.969351ms wall time +GHSA-vcvr-r3jv-pc5j has been filtered out because: next 16.2.0 through 16.3.5 (fixed in 16.3.6): the Node.js ImageResponse of next/og renders values that application code places into SVG content, attributes or styles, and attacker-controlled values there lead to remote code execution (GitHub advisory GHSA-vcvr-r3jv-pc5j, OSV record published 2026-09-30T14:48Z; the Edge implementation and applications that pass no such values are not affected, per the advisory). This config is applied only to the frozen macOS variant evidence/artifacts/macos-application-20260924/variant/pnpm-lock.yaml, which pins 16.3.5 and of which the repository keeps only package.json and this lock (two files, no application source). At 11227bfd, outside the artifact directory itself, git grep finds it only in the scan inventory, in the experiment record blueprints/convergence-practice/application-delivery/experiment-macos-20260924.json and in the evidence manifest (this change's tests, receipt and evidence name it too), and no workflow, script or recipe installs, builds or serves it, so no route can reach ImageResponse from anything this repository runs. The live Next/React recipe lock, blueprints/convergence-practice/application-delivery/pnpm-lock.yaml, pins 16.3.6, which the advisory does not affect, and is scanned under the other config, which has no exception for this advisory. The frozen lock cannot be bumped: its bytes are hash-bound evidence. Evidence: evidence/receipts/osv-urllib3-next-20260930.json. +Filtered 1 vulnerability from output +Starting filesystem walk for root: / +Scanned /blueprints/convergence-practice/wsl-retrieval/package-lock.json file and found 170 packages +End status: 0 dirs visited, 1 inodes visited, 1 Extract calls, 5.46189ms elapsed, 5.46195ms wall time +GHSA-vfj7-8cjw-p6xm and 1 alias have been filtered out because: 2026-10-03 bounded retirement review: this config is assigned only to blueprints/convergence-practice/wsl-retrieval/package-lock.json at SHA256 5c51ee65cc477f2c1488a38ff5cad1c0a737f81a5b61bbd70d5edc4d15bfc3bb. It preserves the original lock as historical evidence; braces 3.0.3 remains affected by GHSA-vfj7-8cjw-p6xm/CVE-2026-93687. Both old source/QMD runner modes stop before subprocess or output creation, and a dependency-free private manifest uses npm 11.19.0 devEngines.runtime.name=retired-wsl-retrieval/onFail=error to reject supported install/ci entry points. Original manifest/recording-aid bytes are archived and original receipts, failure facts and prior runner archives remain unchanged. This is a supported-entry-point guard, not an installation sandbox: explicit --force, other package managers and restored historical files are outside its claim. Source disposition and exact artifact bindings: blueprints/convergence-practice/wsl-retrieval/retirement-assessment.json. Independent native controls and retained failures: evidence/receipts/wsl-retrieval-retirement-20261003.json. Owner handoff: https://github.com/seathatflowsinourveins/native-agent-stack/pull/608#issuecomment-5963764481. The workflow and inventory tests constrain this one input; OSV itself does not constrain an explicit config to that path. Active QMD remains separately unresolved and ordinary/macOS configs carry no exception for this advisory. This exception expires at the native TOML date 2026-10-31; retirement does not patch a dependency or qualify active QMD. +Filtered 1 vulnerability from output diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/final-scan-stdout.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/final-scan-stdout.txt new file mode 100644 index 000000000..2ccb41979 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/final-scan-stdout.txt @@ -0,0 +1,76 @@ +Starting filesystem walk for root: / +Scanned /.github/requirements-ci.txt file and found 1 package +Scanned /blueprints/us-equities/sim-crosscheck-hftbacktest/requirements.lock file and found 43 packages +Scanned /adoption/sdk/accepted-constraints.txt file and found 36 packages +Scanned /adoption/sdk/requirements-linux-x86_64-py313.lock file and found 36 packages +Scanned /blueprints/convergence-practice/application-delivery/pnpm-lock.yaml file and found 109 packages +Scanned /blueprints/convergence-practice/application-delivery/uv.lock file and found 31 packages +Scanned /blueprints/memory-stack/native-qualification/uv.lock file and found 86 packages +Scanned /blueprints/us-equities/adaptive-paper/requirements.txt file and found 2 packages +Scanned /blueprints/us-equities/catalyst-provenance/requirements.lock file and found 42 packages +Scanned /blueprints/us-equities/data/requirements.in file and found 5 packages +Scanned /blueprints/us-equities/data/requirements.lock file and found 21 packages +Scanned /blueprints/runtime-workers/openhands/requirements.lock file and found 334 packages +Scanned /blueprints/runtime-workers/openhands/build-requirements.lock file and found 3 packages +Scanned /blueprints/us-equities/engine/patches/alpaca.packages.lock.json file and found 45 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Algorithm.CSharp/packages.lock.json file and found 30 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Algorithm.Framework/packages.lock.json file and found 26 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Algorithm/packages.lock.json file and found 22 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/AlgorithmFactory/packages.lock.json file and found 23 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Api/packages.lock.json file and found 22 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Brokerages/packages.lock.json file and found 24 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Common/packages.lock.json file and found 20 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Compression/packages.lock.json file and found 6 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Configuration/packages.lock.json file and found 5 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/DownloaderDataProvider/packages.lock.json file and found 37 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Engine/packages.lock.json file and found 36 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Indicators/packages.lock.json file and found 21 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Launcher/packages.lock.json file and found 81 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Logging/packages.lock.json file and found 1 package +Scanned /blueprints/us-equities/engine/patches/lean-locks/Messaging/packages.lock.json file and found 28 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Optimizer.Launcher/packages.lock.json file and found 22 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Optimizer/packages.lock.json file and found 21 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Queues/packages.lock.json file and found 25 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Report/packages.lock.json file and found 48 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Research/packages.lock.json file and found 68 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/Tests/packages.lock.json file and found 97 packages +Scanned /blueprints/us-equities/engine/patches/lean-locks/ToolBox/packages.lock.json file and found 37 packages +Scanned /blueprints/us-equities/research-evaluation/requirements.in file and found 2 packages +Scanned /blueprints/us-equities/research-evaluation/requirements.lock file and found 19 packages +Scanned /blueprints/us-equities/workers/requirements.txt file and found 5 packages +Scanned /tools/mlx-smoke/requirements.in file and found 3 packages +Scanned /tools/mlx-smoke/requirements.lock.txt file and found 34 packages +Scanned /blueprints/gap-wave2-20260923/us-equities__portfolio-risk/requirements-libs.lock file and found 56 packages +Scanned /blueprints/us-equities/mover-v3/data-tools/requirements.lock file and found 9 packages +Scanned /blueprints/us-equities/mover-v3/study/runtime-requirements.txt file and found 10 packages +Scanned /evidence/artifacts/macos-syn-e2e-20260924/requirements-macos-arm64-py312.lock file and found 20 packages +Scanned /blueprints/us-equities/engine-trials/spy-one-zero-20260926/ml4t-backtest/lockcheck/ml4t.lock file and found 15 packages +Scanned /blueprints/us-equities/engine-trials/spy-one-zero-20260926/lumibot/lockcheck/lumibot.lock file and found 264 packages +Scanned /blueprints/us-equities/engine-trials/spy-one-zero-20260926/lumibot/lockcheck/ibapi-build/build.lock file and found 4 packages +End status: 0 dirs visited, 48 inodes visited, 48 Extract calls, 106.198816ms elapsed, 106.198876ms wall time +Filtered 175 local/unscannable package/s from the scan. +GHSA-hj66-6f7g-4r5v and 2 aliases have been filtered out because: oauthlib 0.6.1 through 3.3.1 (fixed in 4.0.0): RevocationEndpoint wraps its response in the request's callback parameter only when constructed with enable_jsonp=True (oauthlib/oauth2/rfc6749/endpoints/revocation.py:72-73 and 81-82 in 3.3.1); oauthlib's pre-configured servers never pass it. Two allowed locks pin 3.3.1: the OpenHands recipe lock and the frozen, evaluation-only Lumibot lock (run offline under bwrap). A text search of one hash-verified artifact per requirement of both locks found no enable_jsonp or create_revocation_response and no importer of oauthlib's endpoints, grant types or servers: oauthlib is imported only by requests-oauthlib 2.0.0 (client classes, token errors and helpers) and, in the Lumibot lock, kubernetes 36.0.3 (an exception class); every other RevocationEndpoint is Authlib's own class, and Authlib does not import oauthlib. Evidence: evidence/receipts/osv-oauthlib-pyjwt-reachability-20260929.json and, for the OpenHands lock at its relocked sha256, evidence/receipts/osv-openhands-pyjwt-relock-20260930.json. Expiry plan: the OpenHands lock was relocked onto PyJWT 2.14.0 on 2026-09-30 and still pins oauthlib 3.3.1; as of 2026-09-30, oauthlib 4.0.0 (PyPI, 2026-09-28T06:01Z) was inside the 7-day window the upstream workspace's exclude-newer applies (until 2026-10-05T06:01:19Z), so the relock onto it follows, tracked in #518 (owner: bc, session native-agent-stack-bc; earliest 2026-10-05T18:40:43Z, when PyJWT 2.15.1 has left its window too; due 2026-10-08), and in the same change deletes the OpenHands entry of IGNORE_ALLOWED_LOCKS, whose sha256 pin fails any change to that lock. The Lumibot lock stays frozen on 3.3.1, so on 2026-10-13 renew this ignore for that lock alone with a new dated reason, or delete that lock. +GHSA-xpv3-w29h-x7cv and 2 aliases have been filtered out because: oauthlib >= 3.0.0, < 4.0.0 (fixed in 4.0.0; the scanned locks pin 3.3.1): an oauthlib authorization server compares the PKCE code_verifier with == (code_challenge_method_s256 at oauthlib/oauth2/rfc6749/grant_types/authorization_code.py:49 and code_challenge_method_plain at :61, in 3.3.1), called only from AuthorizationCodeGrant.validate_token_request (line 506) when a token endpoint handles a request. Neither allowed lock (the OpenHands recipe lock and the frozen Lumibot lock) runs an oauthlib authorization server: in a text search of one hash-verified artifact per requirement of both locks, oauthlib is imported only by requests-oauthlib 2.0.0 (WebApplicationClient, LegacyApplicationClient, the oauth1 Client, token errors and helpers) and, in the Lumibot lock, kubernetes 36.0.3 (an exception class), and nothing imports oauthlib's grant types, endpoints or servers; the PKCE code in openhands-sdk, requests-oauthlib and google-auth-oauthlib only generates a verifier as a client. Evidence: evidence/receipts/osv-oauthlib-pyjwt-reachability-20260929.json and, for the OpenHands lock at its relocked sha256, evidence/receipts/osv-openhands-pyjwt-relock-20260930.json. Expiry plan: as for GHSA-hj66-6f7g-4r5v; the OpenHands relock onto oauthlib 4.0.0 removes that lock's pin, and on 2026-10-13 this ignore is renewed for the frozen Lumibot lock alone or that lock is deleted. +GHSA-8mgp-746c-j5xp and 2 aliases have been filtered out because: nltk through 3.10.3: TransitionParser.train/parse, AveragedPerceptron.save/load, PerceptronTagger.save_to_json and nltk.classify.maxent.save_maxent_params open caller-chosen model paths with the built-in open() instead of the pathsec helpers (the advisory's comparison case is the guarded PerceptronTagger.load_from_json, which in 3.10.3 still authorizes a caller-chosen private model directory), so untrusted input that chooses a model import or export path can read or write outside the allowed roots; pathsec enforcement is on by default (ENFORCE = True in nltk/pathsec.py of 3.10.3). No patched release as of 2026-09-27; the fixes are merged on develop (nltk #3757, #3759, #3813). Each lock allowed to pin nltk is named, with its reviewed sha256 and non-reachability evidence, in IGNORE_ALLOWED_LOCKS in tests/test_osv_lockfile_coverage.py. Lumibot lock: nltk 3.10.3 is a transitive pin (google-adk, llama-index-core) of the evaluation-only Lumibot 4.6.1 environment, run offline under bwrap, and no trial code calls these APIs (evidence: blueprints/us-equities/engine-trials/spy-one-zero-20260926/repository-checks.json). Expiry plan: relock every allowed lock onto the first nltk release that ships those fixes, then delete this ignore; delete the Lumibot lock unless a verdict has adopted Lumibot. +GHSA-hj66-6f7g-4r5v and 2 aliases have been filtered out because: oauthlib 0.6.1 through 3.3.1 (fixed in 4.0.0): RevocationEndpoint wraps its response in the request's callback parameter only when constructed with enable_jsonp=True (oauthlib/oauth2/rfc6749/endpoints/revocation.py:72-73 and 81-82 in 3.3.1); oauthlib's pre-configured servers never pass it. Two allowed locks pin 3.3.1: the OpenHands recipe lock and the frozen, evaluation-only Lumibot lock (run offline under bwrap). A text search of one hash-verified artifact per requirement of both locks found no enable_jsonp or create_revocation_response and no importer of oauthlib's endpoints, grant types or servers: oauthlib is imported only by requests-oauthlib 2.0.0 (client classes, token errors and helpers) and, in the Lumibot lock, kubernetes 36.0.3 (an exception class); every other RevocationEndpoint is Authlib's own class, and Authlib does not import oauthlib. Evidence: evidence/receipts/osv-oauthlib-pyjwt-reachability-20260929.json and, for the OpenHands lock at its relocked sha256, evidence/receipts/osv-openhands-pyjwt-relock-20260930.json. Expiry plan: the OpenHands lock was relocked onto PyJWT 2.14.0 on 2026-09-30 and still pins oauthlib 3.3.1; as of 2026-09-30, oauthlib 4.0.0 (PyPI, 2026-09-28T06:01Z) was inside the 7-day window the upstream workspace's exclude-newer applies (until 2026-10-05T06:01:19Z), so the relock onto it follows, tracked in #518 (owner: bc, session native-agent-stack-bc; earliest 2026-10-05T18:40:43Z, when PyJWT 2.15.1 has left its window too; due 2026-10-08), and in the same change deletes the OpenHands entry of IGNORE_ALLOWED_LOCKS, whose sha256 pin fails any change to that lock. The Lumibot lock stays frozen on 3.3.1, so on 2026-10-13 renew this ignore for that lock alone with a new dated reason, or delete that lock. +GHSA-xpv3-w29h-x7cv and 2 aliases have been filtered out because: oauthlib >= 3.0.0, < 4.0.0 (fixed in 4.0.0; the scanned locks pin 3.3.1): an oauthlib authorization server compares the PKCE code_verifier with == (code_challenge_method_s256 at oauthlib/oauth2/rfc6749/grant_types/authorization_code.py:49 and code_challenge_method_plain at :61, in 3.3.1), called only from AuthorizationCodeGrant.validate_token_request (line 506) when a token endpoint handles a request. Neither allowed lock (the OpenHands recipe lock and the frozen Lumibot lock) runs an oauthlib authorization server: in a text search of one hash-verified artifact per requirement of both locks, oauthlib is imported only by requests-oauthlib 2.0.0 (WebApplicationClient, LegacyApplicationClient, the oauth1 Client, token errors and helpers) and, in the Lumibot lock, kubernetes 36.0.3 (an exception class), and nothing imports oauthlib's grant types, endpoints or servers; the PKCE code in openhands-sdk, requests-oauthlib and google-auth-oauthlib only generates a verifier as a client. Evidence: evidence/receipts/osv-oauthlib-pyjwt-reachability-20260929.json and, for the OpenHands lock at its relocked sha256, evidence/receipts/osv-openhands-pyjwt-relock-20260930.json. Expiry plan: as for GHSA-hj66-6f7g-4r5v; the OpenHands relock onto oauthlib 4.0.0 removes that lock's pin, and on 2026-10-13 this ignore is renewed for the frozen Lumibot lock alone or that lock is deleted. +GHSA-h35f-9h28-mq5c and 3 aliases have been filtered out because: setuptools 80.10.2 in the same frozen evaluation-only lock, run offline under bwrap; the advisory needs an sdist build on macOS APFS/HFS+, while this environment was installed binary-only on Linux (--no-build) and its one source build used setuptools 84.0.0; relocking would change the recorded environment. At expiry, delete the Lumibot lock and these ignores unless Lumibot has been adopted by a verdict. Evidence: blueprints/us-equities/engine-trials/spy-one-zero-20260926/repository-checks.json +Filtered 11 vulnerabilities from output + +No issues found +Starting filesystem walk for root: / +Scanned /evidence/artifacts/macos-application-20260924/variant/pnpm-lock.yaml file and found 109 packages +End status: 0 dirs visited, 1 inodes visited, 1 Extract calls, 2.042931ms elapsed, 2.043001ms wall time +GHSA-vcvr-r3jv-pc5j has been filtered out because: next 16.2.0 through 16.3.5 (fixed in 16.3.6): the Node.js ImageResponse of next/og renders values that application code places into SVG content, attributes or styles, and attacker-controlled values there lead to remote code execution (GitHub advisory GHSA-vcvr-r3jv-pc5j, OSV record published 2026-09-30T14:48Z; the Edge implementation and applications that pass no such values are not affected, per the advisory). This config is applied only to the frozen macOS variant evidence/artifacts/macos-application-20260924/variant/pnpm-lock.yaml, which pins 16.3.5 and of which the repository keeps only package.json and this lock (two files, no application source). At 11227bfd, outside the artifact directory itself, git grep finds it only in the scan inventory, in the experiment record blueprints/convergence-practice/application-delivery/experiment-macos-20260924.json and in the evidence manifest (this change's tests, receipt and evidence name it too), and no workflow, script or recipe installs, builds or serves it, so no route can reach ImageResponse from anything this repository runs. The live Next/React recipe lock, blueprints/convergence-practice/application-delivery/pnpm-lock.yaml, pins 16.3.6, which the advisory does not affect, and is scanned under the other config, which has no exception for this advisory. The frozen lock cannot be bumped: its bytes are hash-bound evidence. Evidence: evidence/receipts/osv-urllib3-next-20260930.json. +Filtered 1 vulnerability from output + +No issues found +Starting filesystem walk for root: / +Scanned /blueprints/convergence-practice/wsl-retrieval/package-lock.json file and found 170 packages +End status: 0 dirs visited, 1 inodes visited, 1 Extract calls, 5.539853ms elapsed, 5.539923ms wall time +GHSA-vfj7-8cjw-p6xm and 1 alias have been filtered out because: 2026-10-03 bounded retirement review: this config is assigned only to blueprints/convergence-practice/wsl-retrieval/package-lock.json at SHA256 5c51ee65cc477f2c1488a38ff5cad1c0a737f81a5b61bbd70d5edc4d15bfc3bb. It preserves the original lock as historical evidence; braces 3.0.3 remains affected by GHSA-vfj7-8cjw-p6xm/CVE-2026-93687. Both old source/QMD runner modes stop before subprocess or output creation, and a dependency-free private manifest uses npm 11.19.0 devEngines.runtime.name=retired-wsl-retrieval/onFail=error to reject supported install/ci entry points. Original manifest/recording-aid bytes are archived and original receipts, failure facts and prior runner archives remain unchanged. This is a supported-entry-point guard, not an installation sandbox: explicit --force, other package managers and restored historical files are outside its claim. Source disposition and exact artifact bindings: blueprints/convergence-practice/wsl-retrieval/retirement-assessment.json. Independent native controls and retained failures: evidence/receipts/wsl-retrieval-retirement-20261003.json. Owner handoff: https://github.com/seathatflowsinourveins/native-agent-stack/pull/608#issuecomment-5963764481. The workflow and inventory tests constrain this one input; OSV itself does not constrain an explicit config to that path. Active QMD remains separately unresolved and ordinary/macOS configs carry no exception for this advisory. This exception expires at the native TOML date 2026-10-31; retirement does not patch a dependency or qualify active QMD. +Filtered 1 vulnerability from output + +No issues found +OSV primary statuses: ordinary=0 frozen-macos=0 frozen-wsl=0 +OSV SARIF statuses: ordinary=0 frozen-macos=0 frozen-wsl=0 diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/final-scan-workflow-scan.sh b/evidence/artifacts/wsl-retrieval-retirement-20261003/final-scan-workflow-scan.sh new file mode 100644 index 000000000..5cf234fc4 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/final-scan-workflow-scan.sh @@ -0,0 +1,67 @@ +# `shell: bash` runs with -e; turn it off so a findings exit (1) is captured and +# all three SARIF results are still written, then fail with the worst observed status. +set +e -u -o pipefail +inventory=.github/osv-scanner-lockfiles.json +frozen_config=.github/osv-scanner-frozen-macos.toml +frozen_wsl_config=.github/osv-scanner-frozen-wsl-retrieval.toml +# Scope comes from this caller, not OSV's explicit-config mechanism. Reject missing/duplicate inputs, +# config drift, changed lock digests, missing evidence, expired policy and restored replay/install routes. +python3 -m unittest \ + tests.test_osv_lockfile_coverage.LockfileInventoryTests \ + tests.test_osv_lockfile_coverage.FrozenScanTests \ + tests.test_wsl_retrieval.RetiredRunnerTests || exit "$?" +# An entry without a parser lets OSV-Scanner infer it from the file name (":"). +# An explicit --config applies to every input of one invocation (OSV-Scanner docs/configuration.md), +# so each frozen group has its own invocation, and ordinary entries use the config with no archive exception. +mapfile -t lockfiles < <(jq -r '.lockfiles[] | select(has("config") | not) | "--lockfile=\(.parser // ""):\(.path)"' "$inventory") +mapfile -t frozen < <(jq -r --arg config "$frozen_config" '.lockfiles[] | select(.config == $config) | "--lockfile=\(.parser // ""):\(.path)"' "$inventory") +mapfile -t frozen_wsl < <(jq -r --arg config "$frozen_wsl_config" '.lockfiles[] | select(.config == $config) | "--lockfile=\(.parser // ""):\(.path)"' "$inventory") +test "${#lockfiles[@]}" -gt 0 || exit 1 +test "${#frozen[@]}" -gt 0 || exit 1 +test "${#frozen_wsl[@]}" -gt 0 || exit 1 +# Every entry is in exactly one scan: together the three lists are the inventory, so an entry that +# names any other config is in neither and fails here. +test "$(( ${#lockfiles[@]} + ${#frozen[@]} + ${#frozen_wsl[@]} ))" -eq "$(jq '.lockfiles | length' "$inventory")" || exit 1 +# Also enforce unique inputs and the two exact archive assignments in the shell caller itself. +jq -e --arg mac_config "$frozen_config" --arg wsl_config "$frozen_wsl_config" ' + .lockfiles as $entries | ($entries | map(.path)) as $paths | + (($paths | length) == ($paths | unique | length)) and + ([$entries[] | select(.config == $mac_config) | .path] == + ["evidence/artifacts/macos-application-20260924/variant/pnpm-lock.yaml"]) and + ([$entries[] | select(.config == $wsl_config) | .path] == + ["blueprints/convergence-practice/wsl-retrieval/package-lock.json"]) +' "$inventory" > /dev/null || exit 1 +# --no-resolve: scan the versions each file pins. Transitive resolution of the +# unlocked manifests reported versions no lockfile installs (decision record). +scan=("$RUNNER_TEMP/osv-scanner/osv-scanner" scan source --config .github/osv-scanner.toml + --no-resolve "${lockfiles[@]}") +scan_frozen=("$RUNNER_TEMP/osv-scanner/osv-scanner" scan source --config "$frozen_config" + --no-resolve "${frozen[@]}") +scan_frozen_wsl=("$RUNNER_TEMP/osv-scanner/osv-scanner" scan source --config "$frozen_wsl_config" + --no-resolve "${frozen_wsl[@]}") +"${scan[@]}" +status=$? +"${scan_frozen[@]}" +frozen_status=$? +"${scan_frozen_wsl[@]}" +frozen_wsl_status=$? +statuses=("$status" "$frozen_status" "$frozen_wsl_status") +printf 'OSV primary statuses: ordinary=%s frozen-macos=%s frozen-wsl=%s\n' "$status" "$frozen_status" "$frozen_wsl_status" +if [ "$WRITE_SARIF" = true ]; then + "${scan[@]}" --format sarif --output-file "$RUNNER_TEMP/osv-scanner/osv-scanner.sarif" + sarif_status=$? + "${scan_frozen[@]}" --format sarif --output-file "$RUNNER_TEMP/osv-scanner/osv-scanner-frozen-macos.sarif" + frozen_sarif_status=$? + "${scan_frozen_wsl[@]}" --format sarif --output-file "$RUNNER_TEMP/osv-scanner/osv-scanner-frozen-wsl-retrieval.sarif" + frozen_wsl_sarif_status=$? + statuses+=("$sarif_status" "$frozen_sarif_status" "$frozen_wsl_sarif_status") + printf 'OSV SARIF statuses: ordinary=%s frozen-macos=%s frozen-wsl=%s\n' "$sarif_status" "$frozen_sarif_status" "$frozen_wsl_sarif_status" + # 0: no vulnerabilities; 1: vulnerabilities (already reported above); other codes are scanner errors. +fi +# Preserve the worst status from every primary and SARIF invocation, including findings and scanner errors. +for code in "${statuses[@]}"; do + if [ "$code" -gt "$status" ]; then + status=$code + fi +done +exit "$status" diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-controls-red-2.json b/evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-controls-red-2.json new file mode 100644 index 000000000..ea13dd998 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-controls-red-2.json @@ -0,0 +1,20 @@ +{ + "executable": "rtk", + "argv": [ + "proxy", + "python3", + "-m", + "unittest", + "tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_original_manifest_is_resolved_without_rewriting_receipt_names", + "tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_changed_original_manifest_archive_rejected", + "tests.test_wsl_retrieval.RetiredRunnerTests", + "-v" + ], + "cwd": "", + "started_utc": "2026-10-03T01:15:19.309Z", + "finished_utc": "2026-10-03T01:15:19.632Z", + "exit_code": 1, + "stdout": "", + "stderr": "test_original_manifest_is_resolved_without_rewriting_receipt_names (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_original_manifest_is_resolved_without_rewriting_receipt_names) ... FAIL\ntest_changed_original_manifest_archive_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_changed_original_manifest_archive_rejected) ... FAIL\ntest_offline_cli_checks_retirement_without_qualifying_history (tests.test_wsl_retrieval.RetiredRunnerTests.test_offline_cli_checks_retirement_without_qualifying_history) ... FAIL\ntest_offline_cli_rejects_changed_recording_aid_archive (tests.test_wsl_retrieval.RetiredRunnerTests.test_offline_cli_rejects_changed_recording_aid_archive) ... FAIL\ntest_offline_cli_rejects_restored_install_routes (tests.test_wsl_retrieval.RetiredRunnerTests.test_offline_cli_rejects_restored_install_routes) ... \n test_offline_cli_rejects_restored_install_routes (tests.test_wsl_retrieval.RetiredRunnerTests.test_offline_cli_rejects_restored_install_routes) (change='missing-runtime-guard') ... FAIL\n test_offline_cli_rejects_restored_install_routes (tests.test_wsl_retrieval.RetiredRunnerTests.test_offline_cli_rejects_restored_install_routes) (change='restored-dependency') ... FAIL\n test_offline_cli_rejects_restored_install_routes (tests.test_wsl_retrieval.RetiredRunnerTests.test_offline_cli_rejects_restored_install_routes) (change='restored-script') ... FAIL\ntest_old_modes_fail_before_subprocess_or_output_creation (tests.test_wsl_retrieval.RetiredRunnerTests.test_old_modes_fail_before_subprocess_or_output_creation) ... \n test_old_modes_fail_before_subprocess_or_output_creation (tests.test_wsl_retrieval.RetiredRunnerTests.test_old_modes_fail_before_subprocess_or_output_creation) (mode='source') ... FAIL\n test_old_modes_fail_before_subprocess_or_output_creation (tests.test_wsl_retrieval.RetiredRunnerTests.test_old_modes_fail_before_subprocess_or_output_creation) (mode='qmd') ... FAIL\n\n======================================================================\nFAIL: test_original_manifest_is_resolved_without_rewriting_receipt_names (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_original_manifest_is_resolved_without_rewriting_receipt_names)\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"/tests/test_wsl_retrieval.py\", line 212, in test_original_manifest_is_resolved_without_rewriting_receipt_names\n result = self.audit(target)\n File \"/tests/test_wsl_retrieval.py\", line 33, in audit\n return MODULE.audit(self.source, self.qmd, self.failed, self.inventory, root)\n ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/blueprints/convergence-practice/wsl-retrieval/audit.py\", line 67, in audit\n require(hashlib.sha256((root/mapping.get(name, name)).read_bytes()).hexdigest() == digest,\n ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n 'changed frozen input: '+name)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/blueprints/convergence-practice/wsl-retrieval/audit.py\", line 51, in require\n raise ValueError(message)\nValueError: changed frozen input: package.json\n\nDuring handling of the above exception, another exception occurred:\n\nTraceback (most recent call last):\n File \"/tests/test_wsl_retrieval.py\", line 214, in test_original_manifest_is_resolved_without_rewriting_receipt_names\n self.fail('historical manifest must resolve to its preserved archive: '+str(exc))\n ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAssertionError: historical manifest must resolve to its preserved archive: changed frozen input: package.json\n\n======================================================================\nFAIL: test_changed_original_manifest_archive_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_changed_original_manifest_archive_rejected)\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"/tests/test_wsl_retrieval.py\", line 225, in test_changed_original_manifest_archive_rejected\n with self.assertRaisesRegex(ValueError, 'changed frozen input: package.json'):\n ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAssertionError: ValueError not raised\n\n======================================================================\nFAIL: test_offline_cli_checks_retirement_without_qualifying_history (tests.test_wsl_retrieval.RetiredRunnerTests.test_offline_cli_checks_retirement_without_qualifying_history)\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"/tests/test_wsl_retrieval.py\", line 326, in test_offline_cli_checks_retirement_without_qualifying_history\n self.assertIn('current_retirement_assessment', assessment)\n ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAssertionError: 'current_retirement_assessment' not found in {'evidence_consistent': True, 'source_checks': 22, 'qmd_checks': 70, 'status': 'incomplete_historical_reference', 'native_acceptance_established': False, 'recorded_successful_attempt_commands': 23, 'retained_failed_commands': 5, 'whole_task_provider_usage': None, 'semantic_rag_or_client_integration': False}\n\n======================================================================\nFAIL: test_offline_cli_rejects_changed_recording_aid_archive (tests.test_wsl_retrieval.RetiredRunnerTests.test_offline_cli_rejects_changed_recording_aid_archive)\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"/tests/test_wsl_retrieval.py\", line 316, in test_offline_cli_rejects_changed_recording_aid_archive\n self.assertEqual(result.returncode, 1)\n ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^\nAssertionError: 0 != 1\n\n======================================================================\nFAIL: test_offline_cli_rejects_restored_install_routes (tests.test_wsl_retrieval.RetiredRunnerTests.test_offline_cli_rejects_restored_install_routes) (change='missing-runtime-guard')\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"/tests/test_wsl_retrieval.py\", line 351, in test_offline_cli_rejects_restored_install_routes\n self.assertIn('retirement manifest', result.stderr)\n ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAssertionError: 'retirement manifest' not found in 'Traceback (most recent call last):\\n File \"/tmp/.ctx-mode-gogfCk/tmpun558uby/leaf/audit.py\", line 168, in \\n print(json.dumps(audit(load(\\'source-receipt.json\\'), load(\\'qmd-receipt.json\\'),\\n ~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\n load(\\'qmd-attempt-1.json\\'), load(\\'install-inventory.json\\')), indent=2))\\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\n File \"/tmp/.ctx-mode-gogfCk/tmpun558uby/leaf/audit.py\", line 67, in audit\\n require(hashlib.sha256((root/mapping.get(name, name)).read_bytes()).hexdigest() == digest,\\n ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\n \\'changed frozen input: \\'+name)\\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\n File \"/tmp/.ctx-mode-gogfCk/tmpun558uby/leaf/audit.py\", line 51, in require\\n raise ValueError(message)\\nValueError: changed frozen input: package.json\\n'\n\n======================================================================\nFAIL: test_offline_cli_rejects_restored_install_routes (tests.test_wsl_retrieval.RetiredRunnerTests.test_offline_cli_rejects_restored_install_routes) (change='restored-dependency')\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"/tests/test_wsl_retrieval.py\", line 351, in test_offline_cli_rejects_restored_install_routes\n self.assertIn('retirement manifest', result.stderr)\n ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAssertionError: 'retirement manifest' not found in 'Traceback (most recent call last):\\n File \"/tmp/.ctx-mode-gogfCk/tmp_xhpdyla/leaf/audit.py\", line 168, in \\n print(json.dumps(audit(load(\\'source-receipt.json\\'), load(\\'qmd-receipt.json\\'),\\n ~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\n load(\\'qmd-attempt-1.json\\'), load(\\'install-inventory.json\\')), indent=2))\\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\n File \"/tmp/.ctx-mode-gogfCk/tmp_xhpdyla/leaf/audit.py\", line 67, in audit\\n require(hashlib.sha256((root/mapping.get(name, name)).read_bytes()).hexdigest() == digest,\\n ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\n \\'changed frozen input: \\'+name)\\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\n File \"/tmp/.ctx-mode-gogfCk/tmp_xhpdyla/leaf/audit.py\", line 51, in require\\n raise ValueError(message)\\nValueError: changed frozen input: package.json\\n'\n\n======================================================================\nFAIL: test_offline_cli_rejects_restored_install_routes (tests.test_wsl_retrieval.RetiredRunnerTests.test_offline_cli_rejects_restored_install_routes) (change='restored-script')\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"/tests/test_wsl_retrieval.py\", line 351, in test_offline_cli_rejects_restored_install_routes\n self.assertIn('retirement manifest', result.stderr)\n ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAssertionError: 'retirement manifest' not found in 'Traceback (most recent call last):\\n File \"/tmp/.ctx-mode-gogfCk/tmpkmrrq7g1/leaf/audit.py\", line 168, in \\n print(json.dumps(audit(load(\\'source-receipt.json\\'), load(\\'qmd-receipt.json\\'),\\n ~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\n load(\\'qmd-attempt-1.json\\'), load(\\'install-inventory.json\\')), indent=2))\\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\n File \"/tmp/.ctx-mode-gogfCk/tmpkmrrq7g1/leaf/audit.py\", line 67, in audit\\n require(hashlib.sha256((root/mapping.get(name, name)).read_bytes()).hexdigest() == digest,\\n ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\n \\'changed frozen input: \\'+name)\\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\n File \"/tmp/.ctx-mode-gogfCk/tmpkmrrq7g1/leaf/audit.py\", line 51, in require\\n raise ValueError(message)\\nValueError: changed frozen input: package.json\\n'\n\n======================================================================\nFAIL: test_old_modes_fail_before_subprocess_or_output_creation (tests.test_wsl_retrieval.RetiredRunnerTests.test_old_modes_fail_before_subprocess_or_output_creation) (mode='source')\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"/tests/test_wsl_retrieval.py\", line 301, in test_old_modes_fail_before_subprocess_or_output_creation\n self.assertFalse(run.exists(), 'retired mode created its output directory')\n ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAssertionError: True is not false : retired mode created its output directory\n\n======================================================================\nFAIL: test_old_modes_fail_before_subprocess_or_output_creation (tests.test_wsl_retrieval.RetiredRunnerTests.test_old_modes_fail_before_subprocess_or_output_creation) (mode='qmd')\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"/tests/test_wsl_retrieval.py\", line 301, in test_old_modes_fail_before_subprocess_or_output_creation\n self.assertFalse(run.exists(), 'retired mode created its output directory')\n ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAssertionError: True is not false : retired mode created its output directory\n\n----------------------------------------------------------------------\nRan 6 tests in 0.216s\n\nFAILED (failures=9)\n", + "publication_policy": "Original worker native command/output record with task-owned paths replaced by role markers. Published digests bind only these published bytes; source tests are authored integration checks, not upstream suites." +} diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-controls-red.json b/evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-controls-red.json new file mode 100644 index 000000000..efa9f81d8 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-controls-red.json @@ -0,0 +1,20 @@ +{ + "executable": "rtk", + "argv": [ + "proxy", + "python3", + "-m", + "unittest", + "tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_original_manifest_is_resolved_without_rewriting_receipt_names", + "tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_changed_original_manifest_archive_rejected", + "tests.test_wsl_retrieval.RetiredRunnerTests", + "-v" + ], + "cwd": "", + "started_utc": "2026-10-03T01:13:37.664Z", + "finished_utc": "2026-10-03T01:13:37.831Z", + "exit_code": 1, + "stdout": "", + "stderr": "test_original_manifest_is_resolved_without_rewriting_receipt_names (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_original_manifest_is_resolved_without_rewriting_receipt_names) ... ERROR\ntest_changed_original_manifest_archive_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_changed_original_manifest_archive_rejected) ... FAIL\ntest_offline_cli_rejects_changed_recording_aid_archive (tests.test_wsl_retrieval.RetiredRunnerTests.test_offline_cli_rejects_changed_recording_aid_archive) ... FAIL\ntest_old_modes_fail_before_subprocess_or_output_creation (tests.test_wsl_retrieval.RetiredRunnerTests.test_old_modes_fail_before_subprocess_or_output_creation) ... \n test_old_modes_fail_before_subprocess_or_output_creation (tests.test_wsl_retrieval.RetiredRunnerTests.test_old_modes_fail_before_subprocess_or_output_creation) (mode='source') ... FAIL\n test_old_modes_fail_before_subprocess_or_output_creation (tests.test_wsl_retrieval.RetiredRunnerTests.test_old_modes_fail_before_subprocess_or_output_creation) (mode='qmd') ... FAIL\n\n======================================================================\nERROR: test_original_manifest_is_resolved_without_rewriting_receipt_names (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_original_manifest_is_resolved_without_rewriting_receipt_names)\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"/tests/test_wsl_retrieval.py\", line 211, in test_original_manifest_is_resolved_without_rewriting_receipt_names\n self.assertTrue(self.audit(target)['evidence_consistent'])\n ~~~~~~~~~~^^^^^^^^\n File \"/tests/test_wsl_retrieval.py\", line 33, in audit\n return MODULE.audit(self.source, self.qmd, self.failed, self.inventory, root)\n ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/blueprints/convergence-practice/wsl-retrieval/audit.py\", line 67, in audit\n require(hashlib.sha256((root/mapping.get(name, name)).read_bytes()).hexdigest() == digest,\n ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n 'changed frozen input: '+name)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/blueprints/convergence-practice/wsl-retrieval/audit.py\", line 51, in require\n raise ValueError(message)\nValueError: changed frozen input: package.json\n\n======================================================================\nFAIL: test_changed_original_manifest_archive_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_changed_original_manifest_archive_rejected)\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"/tests/test_wsl_retrieval.py\", line 221, in test_changed_original_manifest_archive_rejected\n with self.assertRaisesRegex(ValueError, 'changed frozen input: package.json'):\n ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAssertionError: ValueError not raised\n\n======================================================================\nFAIL: test_offline_cli_rejects_changed_recording_aid_archive (tests.test_wsl_retrieval.RetiredRunnerTests.test_offline_cli_rejects_changed_recording_aid_archive)\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"/tests/test_wsl_retrieval.py\", line 312, in test_offline_cli_rejects_changed_recording_aid_archive\n self.assertEqual(result.returncode, 1)\n ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^\nAssertionError: 0 != 1\n\n======================================================================\nFAIL: test_old_modes_fail_before_subprocess_or_output_creation (tests.test_wsl_retrieval.RetiredRunnerTests.test_old_modes_fail_before_subprocess_or_output_creation) (mode='source')\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"/tests/test_wsl_retrieval.py\", line 297, in test_old_modes_fail_before_subprocess_or_output_creation\n self.assertFalse(run.exists(), 'retired mode created its output directory')\n ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAssertionError: True is not false : retired mode created its output directory\n\n======================================================================\nFAIL: test_old_modes_fail_before_subprocess_or_output_creation (tests.test_wsl_retrieval.RetiredRunnerTests.test_old_modes_fail_before_subprocess_or_output_creation) (mode='qmd')\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"/tests/test_wsl_retrieval.py\", line 297, in test_old_modes_fail_before_subprocess_or_output_creation\n self.assertFalse(run.exists(), 'retired mode created its output directory')\n ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAssertionError: True is not false : retired mode created its output directory\n\n----------------------------------------------------------------------\nRan 4 tests in 0.067s\n\nFAILED (failures=4, errors=1)\n", + "publication_policy": "Original worker native command/output record with task-owned paths replaced by role markers. Published digests bind only these published bytes; source tests are authored integration checks, not upstream suites." +} diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-audit-corrected.json b/evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-audit-corrected.json new file mode 100644 index 000000000..cb64e8c73 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-audit-corrected.json @@ -0,0 +1,15 @@ +{ + "executable": "rtk", + "argv": [ + "proxy", + "python3", + "blueprints/convergence-practice/wsl-retrieval/audit.py" + ], + "cwd": "", + "started_utc": "2026-10-03T01:32:03.884Z", + "finished_utc": "2026-10-03T01:32:03.948Z", + "exit_code": 0, + "stdout": "{\n \"evidence_consistent\": true,\n \"source_checks\": 22,\n \"qmd_checks\": 70,\n \"status\": \"incomplete_historical_reference\",\n \"native_acceptance_established\": false,\n \"recorded_successful_attempt_commands\": 23,\n \"retained_failed_commands\": 5,\n \"whole_task_provider_usage\": null,\n \"semantic_rag_or_client_integration\": false,\n \"current_retirement_assessment\": {\n \"artifacts_consistent\": true,\n \"status\": \"retired_historical_source\",\n \"native_npm_guard_acceptance_established\": false,\n \"active_qmd_advisory_status\": \"unresolved\"\n }\n}\n", + "stderr": "", + "publication_policy": "Original worker native command/output record with task-owned paths replaced by role markers. Published digests bind only these published bytes; source tests are authored integration checks, not upstream suites." +} diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-convergence-corrected.json b/evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-convergence-corrected.json new file mode 100644 index 000000000..a49fab13c --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-convergence-corrected.json @@ -0,0 +1,29 @@ +{ + "executable": "rtk", + "argv": [ + "proxy", + "python3", + "scripts/validate_convergence.py", + "blueprints/convergence-practice/wsl-retrieval/experiment.json", + "--root", + ".", + "--json" + ], + "cwd": "", + "started_utc": "2026-10-03T01:27:21.608Z", + "finished_utc": "2026-10-03T01:27:21.678Z", + "exit_code": 0, + "stdout": "{\"records\": [{\"errors\": [], \"observations\": 1, \"path\": \"blueprints/convergence-practice/wsl-retrieval/experiment.json\", \"valid\": true}], \"valid\": true}\n", + "stderr": "", + "correction": { + "error": "npm tag strings did not meet the local convergence revision schema", + "verification_path": [ + "blueprints/convergence-practice/contract.schema.json:57", + "npm11190-tag-ref-0126.json" + ], + "npm_tag": "v11.19.0", + "npm_commit": "bfacd33ccbcd908480610703b60455d2da5b57a9", + "prior_failed_output": "frozen-retrieval-source-final-convergence.json" + }, + "publication_policy": "Original worker native command/output record with task-owned paths replaced by role markers. Published digests bind only these published bytes; source tests are authored integration checks, not upstream suites." +} diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-convergence.json b/evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-convergence.json new file mode 100644 index 000000000..552e6e9e9 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-convergence.json @@ -0,0 +1,19 @@ +{ + "executable": "rtk", + "argv": [ + "proxy", + "python3", + "scripts/validate_convergence.py", + "blueprints/convergence-practice/wsl-retrieval/experiment.json", + "--root", + ".", + "--json" + ], + "cwd": "", + "started_utc": "2026-10-03T01:23:37.786Z", + "finished_utc": "2026-10-03T01:23:37.857Z", + "exit_code": 1, + "stdout": "{\"records\": [{\"errors\": [\"record.primary_sources_and_pins[4].revision: invalid text format\", \"record.primary_sources_and_pins[5].revision: invalid text format\", \"record.primary_sources_and_pins[6].revision: invalid text format\"], \"observations\": 1, \"path\": \"blueprints/convergence-practice/wsl-retrieval/experiment.json\", \"valid\": false}], \"valid\": false}\n", + "stderr": "", + "publication_policy": "Original worker native command/output record with task-owned paths replaced by role markers. Published digests bind only these published bytes; source tests are authored integration checks, not upstream suites." +} diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-registry-validation.json b/evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-registry-validation.json new file mode 100644 index 000000000..4532abc40 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-registry-validation.json @@ -0,0 +1,15 @@ +{ + "executable": "rtk", + "argv": [ + "proxy", + "python3", + "scripts/validate.py" + ], + "cwd": "", + "started_utc": "2026-10-03T01:23:37.857Z", + "finished_utc": "2026-10-03T01:23:59.345Z", + "exit_code": 1, + "stdout": "Publication validation failed:\nblueprints/convergence-practice/wsl-retrieval/README.md: SHA-256 mismatch\nblueprints/convergence-practice/wsl-retrieval/README.md: byte count mismatch\nblueprints/convergence-practice/wsl-retrieval/audit.py: SHA-256 mismatch\nblueprints/convergence-practice/wsl-retrieval/audit.py: byte count mismatch\nblueprints/convergence-practice/wsl-retrieval/experiment.json: SHA-256 mismatch\nblueprints/convergence-practice/wsl-retrieval/experiment.json: byte count mismatch\nblueprints/convergence-practice/wsl-retrieval/package.json: SHA-256 mismatch\nblueprints/convergence-practice/wsl-retrieval/package.json: byte count mismatch\nblueprints/convergence-practice/wsl-retrieval/run.py: SHA-256 mismatch\nblueprints/convergence-practice/wsl-retrieval/run.py: byte count mismatch\ntests/test_wsl_retrieval.py: SHA-256 mismatch\ntests/test_wsl_retrieval.py: byte count mismatch\n", + "stderr": "", + "publication_policy": "Original worker native command/output record with task-owned paths replaced by role markers. Published digests bind only these published bytes; source tests are authored integration checks, not upstream suites." +} diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-tests-corrected.json b/evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-tests-corrected.json new file mode 100644 index 000000000..4bba50674 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-tests-corrected.json @@ -0,0 +1,18 @@ +{ + "executable": "rtk", + "argv": [ + "proxy", + "python3", + "-m", + "unittest", + "tests.test_wsl_retrieval", + "-v" + ], + "cwd": "", + "started_utc": "2026-10-03T01:32:04.097Z", + "finished_utc": "2026-10-03T01:32:04.623Z", + "exit_code": 0, + "stdout": "", + "stderr": "test_exact_argument_boundaries_and_cwd_retained_for_success_and_failure (tests.test_wsl_retrieval.ArchivedCommandRecordingTests.test_exact_argument_boundaries_and_cwd_retained_for_success_and_failure) ... ok\ntest_launch_error_retains_attempted_command (tests.test_wsl_retrieval.ArchivedCommandRecordingTests.test_launch_error_retains_attempted_command) ... ok\ntest_timeout_retains_started_command_and_partial_output (tests.test_wsl_retrieval.ArchivedCommandRecordingTests.test_timeout_retains_started_command_and_partial_output) ... ok\ntest_offline_cli_checks_retirement_without_qualifying_history (tests.test_wsl_retrieval.RetiredRunnerTests.test_offline_cli_checks_retirement_without_qualifying_history) ... ok\ntest_offline_cli_rejects_changed_recording_aid_archive (tests.test_wsl_retrieval.RetiredRunnerTests.test_offline_cli_rejects_changed_recording_aid_archive) ... ok\ntest_offline_cli_rejects_restored_install_routes (tests.test_wsl_retrieval.RetiredRunnerTests.test_offline_cli_rejects_restored_install_routes) ... ok\ntest_old_modes_fail_before_subprocess_or_output_creation (tests.test_wsl_retrieval.RetiredRunnerTests.test_old_modes_fail_before_subprocess_or_output_creation) ... ok\ntest_archived_runner_and_native_receipts_remain_original_bytes (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_archived_runner_and_native_receipts_remain_original_bytes) ... ok\ntest_ast_range_mismatch_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_ast_range_mismatch_rejected) ... ok\ntest_ast_source_bytes_mismatch_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_ast_source_bytes_mismatch_rejected) ... ok\ntest_bounded_get_cannot_return_extra_lines (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_bounded_get_cannot_return_extra_lines) ... ok\ntest_changed_frozen_input_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_changed_frozen_input_rejected) ... ok\ntest_changed_original_manifest_archive_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_changed_original_manifest_archive_rejected) ... ok\ntest_embeddings_or_model_artifact_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_embeddings_or_model_artifact_rejected) ... ok\ntest_error_is_not_successful_absence (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_error_is_not_successful_absence) ... ok\ntest_every_frozen_input_is_required_in_every_attempt (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_every_frozen_input_is_required_in_every_attempt) ... ok\ntest_failed_attempt_cannot_add_an_unrelated_check (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_failed_attempt_cannot_add_an_unrelated_check) ... ok\ntest_incomplete_evidence_does_not_qualify_component_or_adoption (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_incomplete_evidence_does_not_qualify_component_or_adoption) ... ok\ntest_lexical_byte_span_mismatch_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_lexical_byte_span_mismatch_rejected) ... ok\ntest_lexical_source_line_mismatch_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_lexical_source_line_mismatch_rejected) ... ok\ntest_missing_body_or_wrong_line_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_missing_body_or_wrong_line_rejected) ... ok\ntest_missing_failed_commands_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_missing_failed_commands_rejected) ... ok\ntest_negative_cannot_return_cross_scope_content (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_negative_cannot_return_cross_scope_content) ... ok\ntest_optional_inference_backend_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_optional_inference_backend_rejected) ... ok\ntest_original_manifest_is_resolved_without_rewriting_receipt_names (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_original_manifest_is_resolved_without_rewriting_receipt_names) ... ok\ntest_recorded_native_outputs_pass (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_recorded_native_outputs_pass) ... ok\ntest_retained_failure_cannot_be_erased (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_retained_failure_cannot_be_erased) ... ok\ntest_same_count_check_substitution_rejected_for_every_passed_check (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_same_count_check_substitution_rejected_for_every_passed_check) ... ok\ntest_same_count_frozen_input_substitution_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_same_count_frozen_input_substitution_rejected) ... ok\ntest_stale_content_after_update_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_stale_content_after_update_rejected) ... ok\ntest_unknown_usage_or_global_scope_claim_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_unknown_usage_or_global_scope_claim_rejected) ... ok\ntest_unrelated_failure_cannot_be_relabeled_uri_compatibility (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_unrelated_failure_cannot_be_relabeled_uri_compatibility) ... ok\ntest_wrong_named_index_or_collection_rejected (tests.test_wsl_retrieval.WslRetrievalEvidenceTests.test_wrong_named_index_or_collection_rejected) ... ok\n\n----------------------------------------------------------------------\nRan 33 tests in 0.408s\n\nOK\n", + "publication_policy": "Original worker native command/output record with task-owned paths replaced by role markers. Published digests bind only these published bytes; source tests are authored integration checks, not upstream suites." +} diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-runner-controls.json b/evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-runner-controls.json new file mode 100644 index 000000000..737bdeff7 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-runner-controls.json @@ -0,0 +1,68 @@ +{ + "evidence_class": "local_integration_check", + "controls": [ + { + "mode": "source", + "executable": "rtk", + "argv": [ + "proxy", + "python3", + "blueprints/convergence-practice/wsl-retrieval/run.py", + "--mode", + "source", + "--run-dir", + "/source", + "--source", + "blueprints/convergence-practice/wsl-retrieval/seed/planner.py", + "--rg", + "/unused-rg", + "--ast-grep", + "/unused-ast-grep", + "--node", + "/unused-node", + "--package-prefix", + "" + ], + "cwd": "", + "started_utc": "2026-10-03T01:25:38.516Z", + "finished_utc": "2026-10-03T01:25:38.560Z", + "exit_code": 1, + "stdout": "", + "stderr": "WSL retrieval fixture retired: source/QMD replay is disabled. Run audit.py for offline historical consistency checks; see README.md for the separate current QMD recipe and unresolved advisory.\n", + "output_directory_exists": false, + "scratch_entries": [] + }, + { + "mode": "qmd", + "executable": "rtk", + "argv": [ + "proxy", + "python3", + "blueprints/convergence-practice/wsl-retrieval/run.py", + "--mode", + "qmd", + "--run-dir", + "/qmd", + "--source", + "blueprints/convergence-practice/wsl-retrieval/seed/planner.py", + "--rg", + "/unused-rg", + "--ast-grep", + "/unused-ast-grep", + "--node", + "/unused-node", + "--package-prefix", + "" + ], + "cwd": "", + "started_utc": "2026-10-03T01:25:38.562Z", + "finished_utc": "2026-10-03T01:25:38.603Z", + "exit_code": 1, + "stdout": "", + "stderr": "WSL retrieval fixture retired: source/QMD replay is disabled. Run audit.py for offline historical consistency checks; see README.md for the separate current QMD recipe and unresolved advisory.\n", + "output_directory_exists": false, + "scratch_entries": [] + } + ], + "publication_policy": "Original worker native command/output record with task-owned paths replaced by role markers. Published digests bind only these published bytes; source tests are authored integration checks, not upstream suites." +} diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/independent-source-review.md b/evidence/artifacts/wsl-retrieval-retirement-20261003/independent-source-review.md new file mode 100644 index 000000000..70c612ce2 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/independent-source-review.md @@ -0,0 +1,13 @@ +# Independent retirement source and native-control review + +Astra/max read verdict: **ACCEPT for archive eligibility**; final scanner-exception acceptance remains HOLD for the separate partition/full native scan/current-head gates. + +All nine staged source postimages matched the worker handoff and working-tree bytes. Seventeen historical artifacts matched base56473e4b840f0e6940c031801d866e7e9bf29baf; both original-manifest and recording-aid archives are exact source-base copies. Twenty experiment hash bindings resolve. The runner refuses both modes before subprocess/output creation; the audit resolves original manifest bytes without rewriting historical receipt mappings. Original historical/native acceptance limits and activeQMD's unresolved advisory remain explicit. + +Eight supplied native npm records report exit1, empty stdout and EBADDEVENGINES for retired-wsl-retrieval; no other npm error code appears. All eight own fixture manifests/locks match inputs and have no node_modules. Source support: [npm11.19 pre-execution check](https://github.com/npm/cli/blob/v11.19.0/lib/npm.js#L291). This is the supported npm guard scope, excluding --force, other package managers and restored historical source. + +Scanner records correctly distinguish ordinary1/advisory, initial prospective127/parseerror, initial expired127/parseerror, corrected prospective0/filter and corrected expired1/advisory. The initial TOML is syntactically valid with a string date; OSV requires the date type. Originals remain retained. + +**Carry-forward limit:** the three completed scanner controls used the same ordinary-active lock copy. They establish native ignore/expiry behavior, not routing of active versus archived inputs. Explicit disjoint/exhaustive inventory, digest/config/evidence/expiry checks and native final-partition execution must establish routing before exception adoption. The advisory is not patched and active QMD is not qualified. + +Read-only source/artifact verification and Gitqueries0. This review did not rerun the33 authored tests, audit, convergence, npm or scanner commands. Their original native records are separate artifacts. This is a coordinator summary of the independent reader's verdict, not reconstructed native command output or an upstream test receipt. diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/integrated-osv-tests.stderr.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/integrated-osv-tests.stderr.txt new file mode 100644 index 000000000..09e26540d --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/integrated-osv-tests.stderr.txt @@ -0,0 +1,51 @@ +test_a_missing_allowed_lock_is_reported (tests.test_osv_lockfile_coverage.AllowedLockTests.test_a_missing_allowed_lock_is_reported) ... ok +test_allowed_advisories_are_scoped_active_ignores (tests.test_osv_lockfile_coverage.AllowedLockTests.test_allowed_advisories_are_scoped_active_ignores) ... ok +test_an_allowed_lock_is_exempt_only_for_the_advisories_it_lists (tests.test_osv_lockfile_coverage.AllowedLockTests.test_an_allowed_lock_is_exempt_only_for_the_advisories_it_lists) ... ok +test_an_ignore_is_active_only_before_its_ignore_until_date (tests.test_osv_lockfile_coverage.AllowedLockTests.test_an_ignore_is_active_only_before_its_ignore_until_date) ... ok +test_an_include_in_an_allowed_lock_is_flagged_and_not_exempt (tests.test_osv_lockfile_coverage.AllowedLockTests.test_an_include_in_an_allowed_lock_is_flagged_and_not_exempt) ... ok +test_every_allowed_lock_is_an_inventory_lockfile (tests.test_osv_lockfile_coverage.AllowedLockTests.test_every_allowed_lock_is_an_inventory_lockfile) ... ok +test_every_allowed_lock_is_self_contained (tests.test_osv_lockfile_coverage.AllowedLockTests.test_every_allowed_lock_is_self_contained) ... ok +test_every_allowed_lock_matches_its_reviewed_digest (tests.test_osv_lockfile_coverage.AllowedLockTests.test_every_allowed_lock_matches_its_reviewed_digest) ... ok +test_every_allowed_lock_names_existing_evidence (tests.test_osv_lockfile_coverage.AllowedLockTests.test_every_allowed_lock_names_existing_evidence) ... ok +test_the_digest_check_catches_a_changed_byte (tests.test_osv_lockfile_coverage.AllowedLockTests.test_the_digest_check_catches_a_changed_byte) ... ok +test_the_digest_check_reads_every_allowed_lock (tests.test_osv_lockfile_coverage.AllowedLockTests.test_the_digest_check_reads_every_allowed_lock) ... ok +test_the_digest_is_taken_over_bytes_not_text (tests.test_osv_lockfile_coverage.AllowedLockTests.test_the_digest_is_taken_over_bytes_not_text) ... ok +test_each_frozen_config_holds_exactly_the_advisories_of_its_locks (tests.test_osv_lockfile_coverage.FrozenScanTests.test_each_frozen_config_holds_exactly_the_advisories_of_its_locks) ... ok +test_each_frozen_lock_matches_its_reviewed_digest_and_names_evidence (tests.test_osv_lockfile_coverage.FrozenScanTests.test_each_frozen_lock_matches_its_reviewed_digest_and_names_evidence) ... ok +test_expired_ignore_fails_the_policy_before_a_scan (tests.test_osv_lockfile_coverage.FrozenScanTests.test_expired_ignore_fails_the_policy_before_a_scan) ... ok +test_only_the_frozen_locks_name_a_config (tests.test_osv_lockfile_coverage.FrozenScanTests.test_only_the_frozen_locks_name_a_config) ... ok +test_the_ordinary_config_has_no_exception_for_a_frozen_advisory (tests.test_osv_lockfile_coverage.FrozenScanTests.test_the_ordinary_config_has_no_exception_for_a_frozen_advisory) ... ok +test_the_partition_check_catches_a_mutant_inventory (tests.test_osv_lockfile_coverage.FrozenScanTests.test_the_partition_check_catches_a_mutant_inventory) ... ok +test_the_split_is_exhaustive_and_disjoint (tests.test_osv_lockfile_coverage.FrozenScanTests.test_the_split_is_exhaustive_and_disjoint) ... ok +test_the_workflow_scans_each_config_in_its_own_invocation (tests.test_osv_lockfile_coverage.FrozenScanTests.test_the_workflow_scans_each_config_in_its_own_invocation) ... ok +test_wsl_config_is_short_lived_and_names_its_evidence (tests.test_osv_lockfile_coverage.FrozenScanTests.test_wsl_config_is_short_lived_and_names_its_evidence) ... ok +test_wsl_lock_has_a_dedicated_config_and_fixed_identity (tests.test_osv_lockfile_coverage.FrozenScanTests.test_wsl_lock_has_a_dedicated_config_and_fixed_identity) ... ok +test_a_continuation_is_joined_before_a_requirement_is_read (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_a_continuation_is_joined_before_a_requirement_is_read) ... ok +test_a_tracked_version_that_is_not_a_plain_release_fails_closed (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_a_tracked_version_that_is_not_a_plain_release_fails_closed) ... ok +test_a_uv_lock_pin_is_read (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_a_uv_lock_pin_is_read) ... ok +test_an_include_that_names_no_file_fails_closed (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_an_include_that_names_no_file_fails_closed) ... ok +test_documented_options_that_name_no_package_pass (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_documented_options_that_name_no_package_pass) ... ok +test_equality_extras_markers_spacing_and_case_are_read (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_equality_extras_markers_spacing_and_case_are_read) ... ok +test_every_ignore_has_id_reason_and_a_near_expiry (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_every_ignore_has_id_reason_and_a_near_expiry) ... ok +test_includes_are_followed_relative_to_the_including_file (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_includes_are_followed_relative_to_the_including_file) ... ok +test_lines_the_guard_cannot_parse_fail_closed (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_lines_the_guard_cannot_parse_fail_closed) ... ok +test_no_other_suppression_mechanism_bypasses_the_policy (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_no_other_suppression_mechanism_bypasses_the_policy) ... ok +test_repo_wide_ignores_hide_nothing_outside_their_allowed_lock (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_repo_wide_ignores_hide_nothing_outside_their_allowed_lock) ... ok +test_the_ignore_field_check_catches_a_mutant (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_the_ignore_field_check_catches_a_mutant) ... ok +test_the_scope_guard_catches_a_mutant (tests.test_osv_lockfile_coverage.IgnorePolicyTests.test_the_scope_guard_catches_a_mutant) ... ok +test_entries_are_unique_existing_files (tests.test_osv_lockfile_coverage.LockfileInventoryTests.test_entries_are_unique_existing_files) ... ok +test_every_tracked_lockfile_and_manifest_is_listed (tests.test_osv_lockfile_coverage.LockfileInventoryTests.test_every_tracked_lockfile_and_manifest_is_listed) ... ok +test_exclusions_are_reasoned_fixtures_not_scanned_anywhere (tests.test_osv_lockfile_coverage.LockfileInventoryTests.test_exclusions_are_reasoned_fixtures_not_scanned_anywhere) ... ok +test_manifests_without_an_extractor_point_at_a_scanned_lockfile (tests.test_osv_lockfile_coverage.LockfileInventoryTests.test_manifests_without_an_extractor_point_at_a_scanned_lockfile) ... ok +test_parsers_are_explicit_where_osv_cannot_infer_them (tests.test_osv_lockfile_coverage.LockfileInventoryTests.test_parsers_are_explicit_where_osv_cannot_infer_them) ... ok +test_workflow_scans_the_inventory_with_the_config (tests.test_osv_lockfile_coverage.LockfileInventoryTests.test_workflow_scans_the_inventory_with_the_config) ... ok +test_each_primary_and_sarif_status_is_retained (tests.test_osv_lockfile_coverage.SyntheticWorkflowInvocationTests.test_each_primary_and_sarif_status_is_retained) ... ok +test_pr_collects_three_primary_statuses_without_sarif (tests.test_osv_lockfile_coverage.SyntheticWorkflowInvocationTests.test_pr_collects_three_primary_statuses_without_sarif) ... ok +test_preflight_failure_is_returned_before_scan (tests.test_osv_lockfile_coverage.SyntheticWorkflowInvocationTests.test_preflight_failure_is_returned_before_scan) ... ok +test_three_invocations_keep_every_input_and_parser_separate (tests.test_osv_lockfile_coverage.SyntheticWorkflowInvocationTests.test_three_invocations_keep_every_input_and_parser_separate) ... ok +test_unknown_duplicate_missing_and_wrong_archive_assignments_fail_before_scan (tests.test_osv_lockfile_coverage.SyntheticWorkflowInvocationTests.test_unknown_duplicate_missing_and_wrong_archive_assignments_fail_before_scan) ... ok + +---------------------------------------------------------------------- +Ran 46 tests in 2.090s + +OK diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/integrated-osv-tests.stdout.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/integrated-osv-tests.stdout.txt new file mode 100644 index 000000000..e69de29bb diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/macos-portability-correction.md b/evidence/artifacts/wsl-retrieval-retirement-20261003/macos-portability-correction.md new file mode 100644 index 000000000..52fe874ce --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/macos-portability-correction.md @@ -0,0 +1,93 @@ +# Retained macOS failure and scanner shell portability correction + +Prior publication head `b4a7a0c9fb81fd250b5ae5ade5fa5437e79de063`, base +`56473e4b840f0e6940c031801d866e7e9bf29baf`, had seven required passes and one +macOS failure. The original [validate-macos job111120003517](https://github.com/seathatflowsinourveins/native-agent-stack/actions/runs/37093976373/job/111120003517) +ran9822tests in1696.634s: process1,12failures,1328skips. Native watcher exit1 +and fetch/download exits0 are retained. Uploaded artifact +`adoption-bootstrap-full-suite-macos-37093976373-1`, id11264895096, contained +`full-suite-macos.log`1849953bytes/SHA256 +`bc2914b00c2863f53e22d6f0ceab64700f1d78542b33e3f5b7fbc44c574706d6`. +Its original private log is retained; no raw runner paths or conversations +are published. Job's printed tail was insufficient for diagnosis, so this +record uses the uploaded full log, not an inferred cause from a failed badge. + +Eleven failures were existing `SyntheticWorkflowInvocationTests` in +`tests/test_osv_lockfile_coverage.py`: the native macOS shell could not run +`mapfile`; it then reported unbound arrays, returning127 before the synthetic +scanner routing/status assertions could run. Its exact Bash version was not +retained by that job, so the missing builtin alone is not a version assertion. + +The isolated bounded Sol/max repair changes only list loading in the actual +security workflow: `while IFS= read -r`, quoted indexed-array appends and the +original process substitutions replace the three `mapfile` commands. +Element-existence guards intentionally reject an empty group with exit1 +before `nounset` array-length expansion. Every original length/inventory +check, jq selector, preflight, exact archive assignment, config, scan argument, +ordinary input, status aggregation and SARIF path is preserved. No test +assertion or skip is changed. The retained WSL lock remains at +`blueprints/convergence-practice/wsl-retrieval/package-lock.json`,82463bytes, +SHA256 `5c51ee65cc477f2c1488a38ff5cad1c0a737f81a5b61bbd70d5edc4d15bfc3bb`. +No active-input exception, relock or relocation is introduced. + +The syntax follows the worker's actual installed GNU Bash5.2.21 builtin help +and official installed5.2 manual, sections read, arrays, process substitution +and shell parameter expansion. Root independently fetched the maintained +[GNU Bash5.3 source distribution](https://ftp.gnu.org/gnu/bash/bash-5.3.tar.gz), +11355854bytes/SHA256 +`0d5cd86965f869a26cf64f4b71be7b96f90a3ba8b3d74e27e8e9d9d5550f31ba`, +and extracted `doc/bashref.texi`, `builtins/read.def`, `array.c` using native +tar, exit0. Relevant manual locations: while955, read5548 and read-r5629; +`doc/bashref.texi` SHA256 +`f3d37d57a1061e24d266051de9bd47ffa43dc86584afea11576c535ad2be32d5`. +This is source review of supported syntax, not a new tool installation or +older-Bash/macOS execution. The worker's own nominated5.3 fetch failed1 on +DNS, and its actionlint probe failed1; those failures remain distinct from +root's successful source fetch and scoped native lint. + +Original worker execution, source delta at the prior head: + +```text +python3 -m unittest tests.test_osv_lockfile_coverage tests.test_wsl_retrieval tests.test_workflow_hardening tests.test_openhands_lock_binding +exit0;164tests;6.031s;2skips +bash -n (extracted real scanner step) +exit0 +git diff --check +exit0 +``` + +`macos-portability-integration.stdout.txt` preserves the164-test tool output +byte-for-byte:276bytes/SHA256 +`c6a0fc5f9311456adab5889d9dec5921c006fea6533071a90d1f7c4d33d20619`. +Draft correction: the note's initially transcribed stdout digest was wrong; +root derived this digest from original worker event item10 and verified exact +copied bytes before registration. This was a publication correction, not a +rerun. Empty ordinary/frozen-macos/frozen-WSL +groups were separately run through the same recording fixture: all returned1, +no scanner call, no artifact and no stderr. These are repository integration +and synthetic checks, not unchanged upstream scanner tests or Mac acceptance. +Root's already-pinned native actionlint1.17.0 ran the changed workflow exit0, +empty output. Its official archive SHA256 remains +`620abd485a12b6ab1125b844a876414e1d5bd2af8a3125b27f82b01d0d9d6e5a`. + +The twelfth macOS failure was independent of retirement: unchanged +`RunLoop.test_two_sweeps_inside_a_minute_make_one_rss_request_and_one_edgar_cycle` +returned `(0,1)` instead of `(0,2)` at test_incentive_monitor.py1781. Root +verified identical test/runtime blobs across prior head and base: +`b98d7d1de32e42f0382880bb2d8a27d22fdeeff5` and +`3a735363add2dd49ce4d47e6d193b8fd98195a57`. The test uses `--until-et23:59` +and only creates STOP on its second snapshot request, while its existing +Market.run fixture leaves datetime live. Runtime monitor.py1018/2105/2295 +uses the real Eastern date/deadline and exits after writing a first sweep +when that deadline is reached. The job interval03:45:36–04:13:57UTC crosses +23:59Eastern; this is a source-supported deadline-flake inference, not an +observed per-test wall timestamp. The owning trading/monitor lane received +the failure and deterministic-clock repair lead. Root does not edit that +lane, weaken its assertion or waive its required check. + +Original Linux and OSV passes belong to the prior head. A new publication +must obtain its own exact-head review, required checks and other-lane ACK. +No new OSV/provider/host/GPU trial occurred in this bounded local repair. +The earlier receipt-binding and review-environment failures remain in their +separate original records. No failed condition was deleted or silently +reclassified as acceptance. diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/macos-portability-integration.stdout.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/macos-portability-integration.stdout.txt new file mode 100644 index 000000000..7c561787d --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/macos-portability-integration.stdout.txt @@ -0,0 +1,5 @@ +....................................................................................................................ss.............................................. +---------------------------------------------------------------------- +Ran 164 tests in 6.031s + +OK (skipped=2) diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-ignore.native-command.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-ignore.native-command.txt new file mode 100644 index 000000000..2bc54f04c --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-ignore.native-command.txt @@ -0,0 +1,7 @@ +0 verbose cli /bin/node /lib/node_modules/npm/bin/npm-cli.js +1 info using npm@11.19.0 +2 info using node@v24.21.0 +8 verbose argv "ci" "--ignore-scripts" +16 verbose cwd /npm-cases/ci-ignore +20 verbose exit 1 +21 verbose code 1 diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-ignore.stderr.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-ignore.stderr.txt new file mode 100644 index 000000000..8265408f8 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-ignore.stderr.txt @@ -0,0 +1,9 @@ +npm error code EBADDEVENGINES +npm error EBADDEVENGINES The developer of this package has specified the following through devEngines +npm error EBADDEVENGINES Invalid devEngines.runtime +npm error EBADDEVENGINES Invalid name "retired-wsl-retrieval" does not match "node" for "runtime" +npm error EBADDEVENGINES { +npm error EBADDEVENGINES current: { name: 'node', version: 'v24.21.0' }, +npm error EBADDEVENGINES required: { name: 'retired-wsl-retrieval', onFail: 'error' } +npm error EBADDEVENGINES } +npm error A complete log of this run can be found in: /npm-cases/ci-ignore/cache/_logs/2026-10-03T01_31_58_207Z-debug-0.log diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-ignore.stdout.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-ignore.stdout.txt new file mode 100644 index 000000000..e69de29bb diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-prefix-ignore.native-command.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-prefix-ignore.native-command.txt new file mode 100644 index 000000000..551b78a1a --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-prefix-ignore.native-command.txt @@ -0,0 +1,7 @@ +0 verbose cli /bin/node /lib/node_modules/npm/bin/npm-cli.js +1 info using npm@11.19.0 +2 info using node@v24.21.0 +8 verbose argv "ci" "--prefix" "/npm-cases/ci-prefix-ignore" "--ignore-scripts" +16 verbose cwd +20 verbose exit 1 +21 verbose code 1 diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-prefix-ignore.stderr.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-prefix-ignore.stderr.txt new file mode 100644 index 000000000..7a6d3c0f2 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-prefix-ignore.stderr.txt @@ -0,0 +1,9 @@ +npm error code EBADDEVENGINES +npm error EBADDEVENGINES The developer of this package has specified the following through devEngines +npm error EBADDEVENGINES Invalid devEngines.runtime +npm error EBADDEVENGINES Invalid name "retired-wsl-retrieval" does not match "node" for "runtime" +npm error EBADDEVENGINES { +npm error EBADDEVENGINES current: { name: 'node', version: 'v24.21.0' }, +npm error EBADDEVENGINES required: { name: 'retired-wsl-retrieval', onFail: 'error' } +npm error EBADDEVENGINES } +npm error A complete log of this run can be found in: /npm-cases/ci-prefix-ignore/cache/_logs/2026-10-03T01_31_58_213Z-debug-0.log diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-prefix-ignore.stdout.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-prefix-ignore.stdout.txt new file mode 100644 index 000000000..e69de29bb diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-prefix.native-command.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-prefix.native-command.txt new file mode 100644 index 000000000..2526ab1e3 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-prefix.native-command.txt @@ -0,0 +1,7 @@ +0 verbose cli /bin/node /lib/node_modules/npm/bin/npm-cli.js +1 info using npm@11.19.0 +2 info using node@v24.21.0 +8 verbose argv "ci" "--prefix" "/npm-cases/ci-prefix" +16 verbose cwd +20 verbose exit 1 +21 verbose code 1 diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-prefix.stderr.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-prefix.stderr.txt new file mode 100644 index 000000000..ba9b3c9ca --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-prefix.stderr.txt @@ -0,0 +1,9 @@ +npm error code EBADDEVENGINES +npm error EBADDEVENGINES The developer of this package has specified the following through devEngines +npm error EBADDEVENGINES Invalid devEngines.runtime +npm error EBADDEVENGINES Invalid name "retired-wsl-retrieval" does not match "node" for "runtime" +npm error EBADDEVENGINES { +npm error EBADDEVENGINES current: { name: 'node', version: 'v24.21.0' }, +npm error EBADDEVENGINES required: { name: 'retired-wsl-retrieval', onFail: 'error' } +npm error EBADDEVENGINES } +npm error A complete log of this run can be found in: /npm-cases/ci-prefix/cache/_logs/2026-10-03T01_31_58_212Z-debug-0.log diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-prefix.stdout.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-prefix.stdout.txt new file mode 100644 index 000000000..e69de29bb diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci.native-command.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci.native-command.txt new file mode 100644 index 000000000..74ecfa400 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci.native-command.txt @@ -0,0 +1,7 @@ +0 verbose cli /bin/node /lib/node_modules/npm/bin/npm-cli.js +1 info using npm@11.19.0 +2 info using node@v24.21.0 +8 verbose argv "ci" +16 verbose cwd /npm-cases/ci +20 verbose exit 1 +21 verbose code 1 diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci.stderr.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci.stderr.txt new file mode 100644 index 000000000..20435056c --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci.stderr.txt @@ -0,0 +1,9 @@ +npm error code EBADDEVENGINES +npm error EBADDEVENGINES The developer of this package has specified the following through devEngines +npm error EBADDEVENGINES Invalid devEngines.runtime +npm error EBADDEVENGINES Invalid name "retired-wsl-retrieval" does not match "node" for "runtime" +npm error EBADDEVENGINES { +npm error EBADDEVENGINES current: { name: 'node', version: 'v24.21.0' }, +npm error EBADDEVENGINES required: { name: 'retired-wsl-retrieval', onFail: 'error' } +npm error EBADDEVENGINES } +npm error A complete log of this run can be found in: /npm-cases/ci/cache/_logs/2026-10-03T01_31_58_202Z-debug-0.log diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci.stdout.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci.stdout.txt new file mode 100644 index 000000000..e69de29bb diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-ignore.native-command.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-ignore.native-command.txt new file mode 100644 index 000000000..6131b11e5 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-ignore.native-command.txt @@ -0,0 +1,7 @@ +0 verbose cli /bin/node /lib/node_modules/npm/bin/npm-cli.js +1 info using npm@11.19.0 +2 info using node@v24.21.0 +8 verbose argv "install" "--ignore-scripts" +16 verbose cwd /npm-cases/install-ignore +20 verbose exit 1 +21 verbose code 1 diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-ignore.stderr.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-ignore.stderr.txt new file mode 100644 index 000000000..c56178b4d --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-ignore.stderr.txt @@ -0,0 +1,9 @@ +npm error code EBADDEVENGINES +npm error EBADDEVENGINES The developer of this package has specified the following through devEngines +npm error EBADDEVENGINES Invalid devEngines.runtime +npm error EBADDEVENGINES Invalid name "retired-wsl-retrieval" does not match "node" for "runtime" +npm error EBADDEVENGINES { +npm error EBADDEVENGINES current: { name: 'node', version: 'v24.21.0' }, +npm error EBADDEVENGINES required: { name: 'retired-wsl-retrieval', onFail: 'error' } +npm error EBADDEVENGINES } +npm error A complete log of this run can be found in: /npm-cases/install-ignore/cache/_logs/2026-10-03T01_31_58_202Z-debug-0.log diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-ignore.stdout.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-ignore.stdout.txt new file mode 100644 index 000000000..e69de29bb diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-prefix-ignore.native-command.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-prefix-ignore.native-command.txt new file mode 100644 index 000000000..bb61d81cf --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-prefix-ignore.native-command.txt @@ -0,0 +1,7 @@ +0 verbose cli /bin/node /lib/node_modules/npm/bin/npm-cli.js +1 info using npm@11.19.0 +2 info using node@v24.21.0 +8 verbose argv "install" "--prefix" "/npm-cases/install-prefix-ignore" "--ignore-scripts" +16 verbose cwd +20 verbose exit 1 +21 verbose code 1 diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-prefix-ignore.stderr.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-prefix-ignore.stderr.txt new file mode 100644 index 000000000..8edf5528f --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-prefix-ignore.stderr.txt @@ -0,0 +1,9 @@ +npm error code EBADDEVENGINES +npm error EBADDEVENGINES The developer of this package has specified the following through devEngines +npm error EBADDEVENGINES Invalid devEngines.runtime +npm error EBADDEVENGINES Invalid name "retired-wsl-retrieval" does not match "node" for "runtime" +npm error EBADDEVENGINES { +npm error EBADDEVENGINES current: { name: 'node', version: 'v24.21.0' }, +npm error EBADDEVENGINES required: { name: 'retired-wsl-retrieval', onFail: 'error' } +npm error EBADDEVENGINES } +npm error A complete log of this run can be found in: /npm-cases/install-prefix-ignore/cache/_logs/2026-10-03T01_31_58_216Z-debug-0.log diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-prefix-ignore.stdout.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-prefix-ignore.stdout.txt new file mode 100644 index 000000000..e69de29bb diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-prefix.native-command.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-prefix.native-command.txt new file mode 100644 index 000000000..e8bb0c362 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-prefix.native-command.txt @@ -0,0 +1,7 @@ +0 verbose cli /bin/node /lib/node_modules/npm/bin/npm-cli.js +1 info using npm@11.19.0 +2 info using node@v24.21.0 +8 verbose argv "install" "--prefix" "/npm-cases/install-prefix" +16 verbose cwd +20 verbose exit 1 +21 verbose code 1 diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-prefix.stderr.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-prefix.stderr.txt new file mode 100644 index 000000000..b033abc51 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-prefix.stderr.txt @@ -0,0 +1,9 @@ +npm error code EBADDEVENGINES +npm error EBADDEVENGINES The developer of this package has specified the following through devEngines +npm error EBADDEVENGINES Invalid devEngines.runtime +npm error EBADDEVENGINES Invalid name "retired-wsl-retrieval" does not match "node" for "runtime" +npm error EBADDEVENGINES { +npm error EBADDEVENGINES current: { name: 'node', version: 'v24.21.0' }, +npm error EBADDEVENGINES required: { name: 'retired-wsl-retrieval', onFail: 'error' } +npm error EBADDEVENGINES } +npm error A complete log of this run can be found in: /npm-cases/install-prefix/cache/_logs/2026-10-03T01_31_58_213Z-debug-0.log diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-prefix.stdout.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-prefix.stdout.txt new file mode 100644 index 000000000..e69de29bb diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install.native-command.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install.native-command.txt new file mode 100644 index 000000000..858312d50 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install.native-command.txt @@ -0,0 +1,7 @@ +0 verbose cli /bin/node /lib/node_modules/npm/bin/npm-cli.js +1 info using npm@11.19.0 +2 info using node@v24.21.0 +8 verbose argv "install" +16 verbose cwd /npm-cases/install +20 verbose exit 1 +21 verbose code 1 diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install.stderr.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install.stderr.txt new file mode 100644 index 000000000..34e43d9d5 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install.stderr.txt @@ -0,0 +1,9 @@ +npm error code EBADDEVENGINES +npm error EBADDEVENGINES The developer of this package has specified the following through devEngines +npm error EBADDEVENGINES Invalid devEngines.runtime +npm error EBADDEVENGINES Invalid name "retired-wsl-retrieval" does not match "node" for "runtime" +npm error EBADDEVENGINES { +npm error EBADDEVENGINES current: { name: 'node', version: 'v24.21.0' }, +npm error EBADDEVENGINES required: { name: 'retired-wsl-retrieval', onFail: 'error' } +npm error EBADDEVENGINES } +npm error A complete log of this run can be found in: /npm-cases/install/cache/_logs/2026-10-03T01_31_58_213Z-debug-0.log diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install.stdout.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install.stdout.txt new file mode 100644 index 000000000..e69de29bb diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/ordinary-active.stderr.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/ordinary-active.stderr.txt new file mode 100644 index 000000000..17d926748 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/ordinary-active.stderr.txt @@ -0,0 +1,8 @@ +Starting filesystem walk for root: / +Scanned /ordinary-active/package-lock.json file and found 170 packages +End status: 0 dirs visited, 1 inodes visited, 1 Extract calls, 7.083266ms elapsed, 7.083336ms wall time +/.github/osv-scanner.toml has unused ignores: + - GHSA-8mgp-746c-j5xp + - GHSA-h35f-9h28-mq5c + - GHSA-hj66-6f7g-4r5v + - GHSA-xpv3-w29h-x7cv diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/ordinary-active.stdout.json b/evidence/artifacts/wsl-retrieval-retirement-20261003/ordinary-active.stdout.json new file mode 100644 index 000000000..99507be48 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/ordinary-active.stdout.json @@ -0,0 +1,124 @@ +{ + "results": [ + { + "source": { + "path": "/ordinary-active/package-lock.json", + "type": "lockfile" + }, + "packages": [ + { + "package": { + "name": "braces", + "version": "3.0.3", + "ecosystem": "npm" + }, + "groups": [ + { + "ids": [ + "GHSA-vfj7-8cjw-p6xm" + ], + "aliases": [ + "CVE-2026-93687", + "GHSA-vfj7-8cjw-p6xm" + ], + "max_severity": "8.7" + } + ], + "vulnerabilities": [ + { + "affected": [ + { + "database_specific": { + "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/09/GHSA-vfj7-8cjw-p6xm/GHSA-vfj7-8cjw-p6xm.json" + }, + "package": { + "ecosystem": "npm", + "name": "braces", + "purl": "pkg:npm/braces" + }, + "ranges": [ + { + "events": [ + { + "introduced": "0" + }, + { + "last_affected": "3.0.3" + } + ], + "type": "SEMVER" + } + ] + } + ], + "aliases": [ + "CVE-2026-93687" + ], + "database_specific": { + "cwe_ids": [ + "CWE-674" + ], + "github_reviewed": true, + "github_reviewed_at": "2026-10-02T22:36:33Z", + "nvd_published_at": "2026-09-18T16:17:15Z", + "severity": "HIGH" + }, + "details": "braces through 3.0.3 contains a stack overflow vulnerability in the recursive AST walkers that lack depth guards. Attackers can supply deeply nested brace patterns under the character limit to exhaust the call stack and terminate the Node.js process with an uncaught RangeError.", + "id": "GHSA-vfj7-8cjw-p6xm", + "modified": "2026-10-02T22:45:04.328737432Z", + "published": "2026-09-18T18:31:41Z", + "references": [ + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-93687" + }, + { + "type": "WEB", + "url": "https://github.com/micromatch/braces/issues/70" + }, + { + "type": "PACKAGE", + "url": "https://github.com/micromatch/braces" + }, + { + "type": "WEB", + "url": "https://github.com/micromatch/braces/blob/3.0.3/lib/compile.js#L49-L53" + }, + { + "type": "WEB", + "url": "https://github.com/micromatch/braces/blob/3.0.3/lib/expand.js#L102-L105" + }, + { + "type": "WEB", + "url": "https://github.com/micromatch/braces/blob/3.0.3/lib/parse.js#L38-L40" + }, + { + "type": "WEB", + "url": "https://www.vulncheck.com/advisories/braces-through-3.0.3-stack-overflow-via-deeply-nested-patterns" + } + ], + "schema_version": "1.9.0", + "severity": [ + { + "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H", + "type": "CVSS_V3" + }, + { + "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N", + "type": "CVSS_V4" + } + ], + "summary": "braces vulnerable to stack-exhaustion denial of service through deeply nested patterns" + } + ] + } + ] + } + ], + "experimental_config": { + "licenses": { + "summary": false, + "allowlist": null + } + } +} diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/osv-scanner-frozen-macos.sarif b/evidence/artifacts/wsl-retrieval-retirement-20261003/osv-scanner-frozen-macos.sarif new file mode 100644 index 000000000..566e1c2d1 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/osv-scanner-frozen-macos.sarif @@ -0,0 +1,47 @@ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "addresses": [], + "graphs": [], + "invocations": [], + "language": "en-US", + "logicalLocations": [], + "newlineSequences": [ + "\r\n", + "\n" + ], + "policies": [], + "redactionTokens": [], + "results": [], + "runAggregates": [], + "taxonomies": [], + "threadFlowLocations": [], + "tool": { + "driver": { + "contents": [ + "localizedData", + "nonLocalizedData" + ], + "informationUri": "https://github.com/google/osv-scanner", + "isComprehensive": false, + "language": "en-US", + "locations": [], + "name": "osv-scanner", + "notifications": [], + "rules": [], + "supportedTaxonomies": [], + "taxa": [], + "version": "2.6.0" + }, + "extensions": [] + }, + "translations": [], + "versionControlProvenance": [], + "webRequests": [], + "webResponses": [] + } + ], + "properties": {} +} diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/osv-scanner-frozen-wsl-retrieval.sarif b/evidence/artifacts/wsl-retrieval-retirement-20261003/osv-scanner-frozen-wsl-retrieval.sarif new file mode 100644 index 000000000..566e1c2d1 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/osv-scanner-frozen-wsl-retrieval.sarif @@ -0,0 +1,47 @@ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "addresses": [], + "graphs": [], + "invocations": [], + "language": "en-US", + "logicalLocations": [], + "newlineSequences": [ + "\r\n", + "\n" + ], + "policies": [], + "redactionTokens": [], + "results": [], + "runAggregates": [], + "taxonomies": [], + "threadFlowLocations": [], + "tool": { + "driver": { + "contents": [ + "localizedData", + "nonLocalizedData" + ], + "informationUri": "https://github.com/google/osv-scanner", + "isComprehensive": false, + "language": "en-US", + "locations": [], + "name": "osv-scanner", + "notifications": [], + "rules": [], + "supportedTaxonomies": [], + "taxa": [], + "version": "2.6.0" + }, + "extensions": [] + }, + "translations": [], + "versionControlProvenance": [], + "webRequests": [], + "webResponses": [] + } + ], + "properties": {} +} diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/osv-scanner.sarif b/evidence/artifacts/wsl-retrieval-retirement-20261003/osv-scanner.sarif new file mode 100644 index 000000000..566e1c2d1 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/osv-scanner.sarif @@ -0,0 +1,47 @@ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "addresses": [], + "graphs": [], + "invocations": [], + "language": "en-US", + "logicalLocations": [], + "newlineSequences": [ + "\r\n", + "\n" + ], + "policies": [], + "redactionTokens": [], + "results": [], + "runAggregates": [], + "taxonomies": [], + "threadFlowLocations": [], + "tool": { + "driver": { + "contents": [ + "localizedData", + "nonLocalizedData" + ], + "informationUri": "https://github.com/google/osv-scanner", + "isComprehensive": false, + "language": "en-US", + "locations": [], + "name": "osv-scanner", + "notifications": [], + "rules": [], + "supportedTaxonomies": [], + "taxa": [], + "version": "2.6.0" + }, + "extensions": [] + }, + "translations": [], + "versionControlProvenance": [], + "webRequests": [], + "webResponses": [] + } + ], + "properties": {} +} diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/prospective-archive-attempt2.stderr.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/prospective-archive-attempt2.stderr.txt new file mode 100644 index 000000000..81af0c569 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/prospective-archive-attempt2.stderr.txt @@ -0,0 +1,5 @@ +Starting filesystem walk for root: / +Scanned /ordinary-active/package-lock.json file and found 170 packages +End status: 0 dirs visited, 1 inodes visited, 1 Extract calls, 5.545828ms elapsed, 5.545918ms wall time +GHSA-vfj7-8cjw-p6xm and 1 alias have been filtered out because: Prospective control for the hash-bound retired historical WSL retrieval lock only (SHA2565c51ee65cc477f2c1488a38ff5cad1c0a737f81a5b61bbd70d5edc4d15bfc3bb), pending actual source-retirement and native-control acceptance; not an active QMD exception or vulnerability fix. Root scoped handoff608#5963764481 and Astra retirement design acceptance require exact inventory, digest, historical preservation, expiry and ordinary active-input controls before adoption. braces3.0.3 remains affected by GHSA-vfj7-8cjw-p6xm/CVE-2026-93687; no patched release in reviewed primary evidence. This scratch config tests native ignore/expiry behavior only. +Filtered 1 vulnerability from output diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/prospective-archive-attempt2.stdout.json b/evidence/artifacts/wsl-retrieval-retirement-20261003/prospective-archive-attempt2.stdout.json new file mode 100644 index 000000000..939d28473 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/prospective-archive-attempt2.stdout.json @@ -0,0 +1,9 @@ +{ + "results": [], + "experimental_config": { + "licenses": { + "summary": false, + "allowlist": null + } + } +} diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/prospective-archive.stderr.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/prospective-archive.stderr.txt new file mode 100644 index 000000000..6c84503b8 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/prospective-archive.stderr.txt @@ -0,0 +1,2 @@ +Failed to read config file: toml: line 6 (last key "IgnoredVulns.ignoreUntil"): parsing time "2026-10-31" as "2006-01-02T15:04:05Z07:00": cannot parse "" as "T" +toml: line 6 (last key "IgnoredVulns.ignoreUntil"): parsing time "2026-10-31" as "2006-01-02T15:04:05Z07:00": cannot parse "" as "T" diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/prospective-archive.stdout.txt b/evidence/artifacts/wsl-retrieval-retirement-20261003/prospective-archive.stdout.txt new file mode 100644 index 000000000..e69de29bb diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/publication-correction.md b/evidence/artifacts/wsl-retrieval-retirement-20261003/publication-correction.md new file mode 100644 index 000000000..5f5a87ef5 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/publication-correction.md @@ -0,0 +1,9 @@ +# Empty-output publication correction, 2026-10-03 + +Independent artifact review found that ten published stdout files contained one newline while the original native outputs were empty. The declared zero-byte lengths and SHA-256 `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855` correctly described the originals but did not describe those draft copies. This was a publication error, not a new execution result. + +Affected files: the eight `npm-*.stdout.txt` files and the first-attempt `prospective-archive.stdout.txt` and `expired-archive.stdout.txt`. The coordinator independently inspected each original capture, verified that all ten were zero bytes, and restored each published file to those exact empty bytes. The failed scanner attempts and their stderr/configuration records remain retained. + +Verification path: compare every `output_files` length and SHA-256 in `controls.json` against the actual published bytes, then run repository evidence validation. Do not normalize native empty output into a newline when copying evidence. Hash checks establish artifact identity, not native success or archival scanner eligibility. + +The completion candidate's first publication validation also reported `integrated-osv-tests.stdout.txt`. An initial inference that this was an eleventh newline-only copy was wrong: direct byte/hash inspection found both original and public files already empty. Its evidence-registry row was stale instead. Refresh that row from the existing empty bytes; do not describe this as a new output-copy repair. The failed validation output is retained in the completion checks, and this correction records the verification path. diff --git a/evidence/artifacts/wsl-retrieval-retirement-20261003/receipt-binding-correction.md b/evidence/artifacts/wsl-retrieval-retirement-20261003/receipt-binding-correction.md new file mode 100644 index 000000000..b4c02a644 --- /dev/null +++ b/evidence/artifacts/wsl-retrieval-retirement-20261003/receipt-binding-correction.md @@ -0,0 +1,11 @@ +# Explicit retirement receipt lock binding + +The full [Linux suite on PR622 head `3f862a489f1fd0c397169d684cb4657ad90d260f`](https://github.com/seathatflowsinourveins/native-agent-stack/actions/runs/37091096288/job/111111501343) returned one failure after 9,822 tests, 967 skips and 1,654.040 seconds. Its test step exited 1. The original failed-job output retrieved by `gh run view 37091096288 --job 111111501343 --log-failed` is 130,044 bytes, SHA256 `7ac05f4cc48132ca67c5f1b6e13ac6a7021779eb77485de5c4480176817bd2d4`; the retrieval itself exited 0. + +The unchanged `tests.test_openhands_lock_binding.GrantEvidenceTests.test_each_grant_is_recorded_by_the_receipt_it_names` requires the named receipt itself to contain both the complete lock path and its granted digest. The retirement receipt listed the path in `evidence_paths`, while the exact digest appeared only in linked assessment/control records. Following a link does not satisfy this check. The earlier selected 161-test suite omitted this module; its passing result remains valid only for its named scope. + +An isolated clean checkout reproduced the exact failing test: one test, exit 1, same missing-binding assertion. Adding an explicit `locks[]` object to the retirement receipt records the actual unchanged path and SHA256. This reuses the existing frozen-macOS receipt's [lock/digest pattern](https://github.com/seathatflowsinourveins/native-agent-stack/blob/3f862a489f1fd0c397169d684cb4657ad90d260f/evidence/receipts/osv-urllib3-next-20260930.json#L112) and preserves the [binding test](https://github.com/seathatflowsinourveins/native-agent-stack/blob/3f862a489f1fd0c397169d684cb4657ad90d260f/tests/test_openhands_lock_binding.py#L19) unchanged. An intermediate `path` field passed the three-test module; the final field is `lock`, matching the existing reference. This metadata correction does not relock, relocate the lock or change scanner policy, inputs, tests or expiry. + +Native `sha256sum blueprints/convergence-practice/wsl-retrieval/package-lock.json` exited 0 with `5c51ee65cc477f2c1488a38ff5cad1c0a737f81a5b61bbd70d5edc4d15bfc3bb`. The receipt's own registration is refreshed with the repository's supported `host_receipts.register_file`. The completion record's nine inputs and 21 outputs do not bind receipt bytes; they remain unchanged. Its earlier native scan is reused within that exact input scope, not reported as a rerun. Original source acceptance at `3f862a4` does not accept this new publication delta; its current-head review and hosted checks are separate gates. + +Actual red and intermediate green command outputs are retained in the private coordination record. Subsequent focused-suite, convergence, integrity and hosted outputs must be read before their result is claimed. This note preserves the discovered failure and correction; it is not an upstream suite receipt or broad host/provider acceptance. diff --git a/evidence/receipts/wsl-retrieval-retirement-20261003.json b/evidence/receipts/wsl-retrieval-retirement-20261003.json new file mode 100644 index 000000000..1b2ac4628 --- /dev/null +++ b/evidence/receipts/wsl-retrieval-retirement-20261003.json @@ -0,0 +1,75 @@ +{ + "schema_version": 1, + "id": "wsl-retrieval-retirement-20261003", + "kind": "native_cli_e2e", + "component_ids": [ + "qmd" + ], + "recorded_at_utc": "2026-10-03T02:00:21Z", + "evidence_class": "native_cli_integration_controls", + "claim": "The historical WSL retrieval fixture's supported run/install entry points are retired while its original records and lock bytes remain inspectable. Eight npm 11.19.0 controls refuse with EBADDEVENGINES. The strengthened exact workflow scans all 50 inventoried inputs in disjoint groups (48 ordinary, one macOS archive, one retired WSL archive): all three primary and three SARIF invocations return 0 under OSV 2.6.0, with zero SARIF results. The unchanged active-copy input under ordinary config and the final archive config with an expired date both return 1 for GHSA-vfj7-8cjw-p6xm. The integrated source/policy suite runs 161 tests, passing with two skips. The lock stays unchanged at its original path. Active QMD remains affected; current-head hosted CI and independent integration review are separate gates.", + "limitations": [ + "Native CLI integration controls and offline artifact checks, not unchanged upstream test suites, model/provider execution, new-host acceptance or a general installation sandbox.", + "Preserve the original incomplete20260920 assessment, historical command facts, five failed commands and all original archives/lock byte-for-byte. Missing historical argv/cwd/raw logs remain unresolved. The former future recording aid is archived without historical execution attribution.", + "npm controls cover eight supported npm11.19 install/ci/prefix/ignore-scripts combinations only. --force, other package managers and restored historical sources remain outside the guard. Removing package.json alone does not prevent standalone-lock interpretation.", + "Original quoted-TOML-date scanner controls returned127 parse errors. Their outputs/configs are retained; one supported unquoted-date correction preceded the successful prospective/expiry controls.", + "Prospective scratch configs test native ignore/expiry behavior only. Dedicated configs affect every invocation input; explicit workflow inventory partitions, exact lock hashes, evidence binding and expiry controls enforce scope. Final repository partition/full-scan/current-head CI and review are separate evidence.", + "The frozen lock retains braces3.0.3 affected by GHSA-vfj7-8cjw-p6xm/CVE-2026-93687. No patch or active QMD acceptance is claimed; current general QMD use requires separate resolution.", + "All output digests bind published sanitized bytes only. Private task paths are role markers; original native output remains outside the checkout. Selected npm debug-log lines observe original argv/cwd with sanitized paths. No exhaustive filesystem audit or kernel trace.", + "No OS/security grants, target setup, service/timer operation, credential inspection, broker call or model run occurred. Whole-task provider usage and billing are unknown.", + "The earlier final-native-scan-record.json remains historical output for its bound earlier workflow/config. completion-native-record.json records the strengthened partition, shortened 2026-10-17 expiry, fresh active/expired controls and 161-test run; neither is an unchanged upstream test suite. The first completion validation failed on a stale hash row for an already-empty output and is retained, with the corrected diagnosis in publication-correction.md." + ], + "source_base_revision": "56473e4b840f0e6940c031801d866e7e9bf29baf", + "locks": [ + { + "lock": "blueprints/convergence-practice/wsl-retrieval/package-lock.json", + "sha256": "5c51ee65cc477f2c1488a38ff5cad1c0a737f81a5b61bbd70d5edc4d15bfc3bb" + } + ], + "historical_assessment": "evidence/receipts/wsl-retrieval-20260920.json", + "retirement_assessment": "blueprints/convergence-practice/wsl-retrieval/retirement-assessment.json", + "native_control_record": "evidence/artifacts/wsl-retrieval-retirement-20261003/controls.json", + "evidence_paths": [ + "blueprints/convergence-practice/wsl-retrieval/retirement-assessment.json", + "blueprints/convergence-practice/wsl-retrieval/package-original.json.txt", + "blueprints/convergence-practice/wsl-retrieval/run-recording-aid.py.txt", + "blueprints/convergence-practice/wsl-retrieval/package-lock.json", + "evidence/artifacts/wsl-retrieval-retirement-20261003/controls.json", + "evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-runner-controls.json", + "evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-tests-corrected.json", + "evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-audit-corrected.json", + "evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-convergence-corrected.json", + "evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-controls-red.json", + "evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-controls-red-2.json", + "evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-convergence.json", + "evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-registry-validation.json", + "evidence/artifacts/wsl-retrieval-retirement-20261003/independent-source-review.md", + "evidence/artifacts/wsl-retrieval-retirement-20261003/final-native-scan-record.json", + "evidence/artifacts/wsl-retrieval-retirement-20261003/completion-native-record.json", + "evidence/artifacts/wsl-retrieval-retirement-20261003/publication-correction.md" + ], + "primary_sources": [ + { + "repository": "npm/cli", + "revision": "bfacd33ccbcd908480610703b60455d2da5b57a9", + "url": "https://github.com/npm/cli/blob/v11.19.0/docs/lib/content/configuring-npm/package-json.md#devengines" + }, + { + "repository": "npm/cli", + "revision": "bfacd33ccbcd908480610703b60455d2da5b57a9", + "url": "https://github.com/npm/cli/blob/v11.19.0/lib/base-cmd.js#L201" + }, + { + "repository": "google/osv-scanner", + "pin": "v2.6.0", + "url": "https://github.com/google/osv-scanner/releases/tag/v2.6.0", + "binary_sha256": "ca69b3d3cd08f889a49dc0a383122f71cc528b83803671df5fd874d97485b108" + }, + { + "repository": "github/advisory-database", + "url": "https://github.com/advisories/GHSA-vfj7-8cjw-p6xm" + } + ], + "whole_task_provider_usage": null, + "current_native_control_record": "evidence/artifacts/wsl-retrieval-retirement-20261003/completion-native-record.json" +} diff --git a/manifests/evidence.json b/manifests/evidence.json index 5b0b04d20..d2e226e2a 100644 --- a/manifests/evidence.json +++ b/manifests/evidence.json @@ -3880,6 +3880,26 @@ "No production-quality or token-efficiency comparison is claimed, and no unrelated session or authentication store was read." ], "path": "evidence/receipts/codex-01593-hindsight-native-memory-20261001.json" + }, + { + "id": "wsl-retrieval-retirement-20261003", + "kind": "native_cli_e2e", + "component_ids": [ + "qmd" + ], + "claim": "The historical WSL retrieval fixture's supported run/install entry points are retired while its original records and lock bytes remain inspectable. Eight npm 11.19.0 controls refuse with EBADDEVENGINES. The strengthened exact workflow scans all 50 inventoried inputs in disjoint groups (48 ordinary, one macOS archive, one retired WSL archive): all three primary and three SARIF invocations return 0 under OSV 2.6.0, with zero SARIF results. The unchanged active-copy input under ordinary config and the final archive config with an expired date both return 1 for GHSA-vfj7-8cjw-p6xm. The integrated source/policy suite runs 161 tests, passing with two skips. The lock stays unchanged at its original path. Active QMD remains affected; current-head hosted CI and independent integration review are separate gates.", + "limitations": [ + "Native CLI integration controls and offline artifact checks, not unchanged upstream test suites, model/provider execution, new-host acceptance or a general installation sandbox.", + "Preserve the original incomplete20260920 assessment, historical command facts, five failed commands and all original archives/lock byte-for-byte. Missing historical argv/cwd/raw logs remain unresolved. The former future recording aid is archived without historical execution attribution.", + "npm controls cover eight supported npm11.19 install/ci/prefix/ignore-scripts combinations only. --force, other package managers and restored historical sources remain outside the guard. Removing package.json alone does not prevent standalone-lock interpretation.", + "Original quoted-TOML-date scanner controls returned127 parse errors. Their outputs/configs are retained; one supported unquoted-date correction preceded the successful prospective/expiry controls.", + "Prospective scratch configs test native ignore/expiry behavior only. Dedicated configs affect every invocation input; explicit workflow inventory partitions, exact lock hashes, evidence binding and expiry controls enforce scope. Final repository partition/full-scan/current-head CI and review are separate evidence.", + "The frozen lock retains braces3.0.3 affected by GHSA-vfj7-8cjw-p6xm/CVE-2026-93687. No patch or active QMD acceptance is claimed; current general QMD use requires separate resolution.", + "All output digests bind published sanitized bytes only. Private task paths are role markers; original native output remains outside the checkout. Selected npm debug-log lines observe original argv/cwd with sanitized paths. No exhaustive filesystem audit or kernel trace.", + "No OS/security grants, target setup, service/timer operation, credential inspection, broker call or model run occurred. Whole-task provider usage and billing are unknown.", + "The earlier final-native-scan-record.json remains historical output for its bound earlier workflow/config. completion-native-record.json records the strengthened partition, shortened 2026-10-17 expiry, fresh active/expired controls and 161-test run; neither is an unchanged upstream test suite. The first completion validation failed on a stale hash row for an already-empty output and is retained, with the corrected diagnosis in publication-correction.md." + ], + "path": "evidence/receipts/wsl-retrieval-retirement-20261003.json" } ], "files": [ @@ -3923,10 +3943,15 @@ "sha256": "6b093b4320cc481c4b1d5722648ab37fba4a5b50d0553c62c936e1a78e0edbf2", "bytes": 2547 }, + { + "path": ".github/osv-scanner-frozen-wsl-retrieval.toml", + "sha256": "8931d59b82ce8326ea0028adef428a1262f3e16800d1b744341960513a139887", + "bytes": 2330 + }, { "path": ".github/osv-scanner-lockfiles.json", - "sha256": "2ee4a6de196999b33a1f37bb979076308d1a805efd96c0f28f9c23ee6c9e5c22", - "bytes": 10639 + "sha256": "51dae9e9907bda6c3a5de21c1c3dfdea79c5e0a379494ba4c6e42906abc6e114", + "bytes": 10704 }, { "path": ".github/osv-scanner.toml", @@ -4005,8 +4030,8 @@ }, { "path": ".github/workflows/security-scan.yml", - "sha256": "1df67c92865aa16f53cbb9b451fa3fa82a83a50950dc20a8efeea227f93f6240", - "bytes": 11346 + "sha256": "76219f352b6eee1e498e7bcea91daaffc52a235ebf908e53fbf1e2e75e6a607a", + "bytes": 14553 }, { "path": ".github/workflows/supply-chain.yml", @@ -6621,18 +6646,18 @@ }, { "path": "blueprints/convergence-practice/wsl-retrieval/README.md", - "sha256": "e15a193803dfee256bd81d303ff1b309820b42ca16b2af9252f66d7137a494b3", - "bytes": 6575 + "sha256": "0c521bfbee47c91382f20b8cdf1bfcb7377d16cc981354f3da9690ba8435a4dd", + "bytes": 8389 }, { "path": "blueprints/convergence-practice/wsl-retrieval/audit.py", - "sha256": "46cbcaecafe6d4bba3b09a26aa9770667eefbc26d7bbb806d4f0ab98078e86fd", - "bytes": 11713 + "sha256": "da2c4d58d7012febb1ce70d49111ecdbf22f46867fe84c098e01c75c7af0eabd", + "bytes": 14819 }, { "path": "blueprints/convergence-practice/wsl-retrieval/experiment.json", - "sha256": "a029dc2d8de56dd387202e5bffc341ad9d0a163e93f7bc01954b932f4ecf0244", - "bytes": 11528 + "sha256": "fb67f4143ffdb3593caba0f725a1a264cb13cb05bba23ec9bd2ecb0d5a4db669", + "bytes": 14342 }, { "path": "blueprints/convergence-practice/wsl-retrieval/install-inventory.json", @@ -6655,10 +6680,15 @@ "bytes": 82463 }, { - "path": "blueprints/convergence-practice/wsl-retrieval/package.json", + "path": "blueprints/convergence-practice/wsl-retrieval/package-original.json.txt", "sha256": "7bbf63c5eafd347ae5ae56c684be06ef2589d38f2aab580ca7986ca4122bc6a8", "bytes": 174 }, + { + "path": "blueprints/convergence-practice/wsl-retrieval/package.json", + "sha256": "69ed6c37f8517e712ce64e1550c363926bdf25e42d8bfb30efa02b71197f3485", + "bytes": 283 + }, { "path": "blueprints/convergence-practice/wsl-retrieval/pins.json", "sha256": "19572d05638badc6a79abf392924261836e08b19bd01231372e2f764502d393d", @@ -6674,6 +6704,11 @@ "sha256": "cad5b3dc802323e5aad8fcdb4ca98f7d1c3e92749b8e891dc32149c70d3f9527", "bytes": 12226 }, + { + "path": "blueprints/convergence-practice/wsl-retrieval/retirement-assessment.json", + "sha256": "88f1919546dcf8b9fda7b425c7d3ea2ea114b975ccdc6356e0775a3f4e81a9f0", + "bytes": 4162 + }, { "path": "blueprints/convergence-practice/wsl-retrieval/run-initial.py.txt", "sha256": "50012822197f88a736b1eddfa7f3b81a425a86af183051cc3da06bc0514cb2f0", @@ -6685,10 +6720,15 @@ "bytes": 15192 }, { - "path": "blueprints/convergence-practice/wsl-retrieval/run.py", + "path": "blueprints/convergence-practice/wsl-retrieval/run-recording-aid.py.txt", "sha256": "be852ce99501f5bc4567b846b90fb0e91d91de77b0eafbd3b72bb4484a2f7d12", "bytes": 16427 }, + { + "path": "blueprints/convergence-practice/wsl-retrieval/run.py", + "sha256": "0df1c6873b8514cb08af2efebe479dfe46c35ad1e6b05d73796bee0d7cca702e", + "bytes": 722 + }, { "path": "blueprints/convergence-practice/wsl-retrieval/seed/corpus/decoy/other.md", "sha256": "84ecfac576f7486f15b9b601727244055bdaa92cbb9c3387655ee3f6546e7136", @@ -15546,8 +15586,8 @@ }, { "path": "docs/decisions/2026-09-22-github-automation-closure.md", - "sha256": "a39d7d1dc38a537693e7bbbe3ee1c190c87414c659e4162f50c3f2200890ce16", - "bytes": 144342 + "sha256": "82695fb0f06eb10ab874ecc1f592b0f8baf4c793547c3b4d52719a5c4c91867f", + "bytes": 147084 }, { "path": "docs/decisions/2026-09-24-secret-storage.md", @@ -15791,8 +15831,8 @@ }, { "path": "docs/ecosystem/manifest.json", - "sha256": "0885a5ef19e36623e41104fd8d32286ad875bcf1f1e49fcf8a8313db09600fa5", - "bytes": 41473 + "sha256": "905756238461861c5d2a8ca3b762e836ce0f1915abf4050e6b86690573668c4a", + "bytes": 41562 }, { "path": "docs/ecosystem/readiness-observation.json", @@ -46289,6 +46329,441 @@ "sha256": "e0702904c21f7ad253f4ccff12bb27a82a59f1620408cdb0b65d5532176717dd", "bytes": 12602 }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/candidate-archive-expired-invalid-attempt1.toml", + "bytes": 804, + "sha256": "2ac5bf924d35b1b986ca8da16edbb98d0870de7e8f2ce93506edeb4447227461" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/candidate-archive-expired.toml", + "bytes": 802, + "sha256": "cd3582604c56c2710df4054ba10d64ca092974c7edcfbdc744bdee0465582b42" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/candidate-archive-invalid-attempt1.toml", + "bytes": 956, + "sha256": "e9d061af04f9ad915ae903091eed0b5c38959a69b21f2645e8d18b00417d7c6c" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/candidate-archive.toml", + "bytes": 954, + "sha256": "bdf702621ea05990da2c6f57cdf76beb81dad3dadaaf9cf879c950419c57f22a" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/ci-correction.md", + "sha256": "27a2d3ca147c79e41ebbd5e1a9cc952b0d6891e6482fd6ae3b7c566751d8916a", + "bytes": 2488 + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/completion-active-control.exit.txt", + "bytes": 2, + "sha256": "4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/completion-active-control.stderr.txt", + "bytes": 350, + "sha256": "cea7753846753213dc346b68771a0426205224c064b69faca0b2df5059f100b1" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/completion-active-control.stdout.json", + "bytes": 4223, + "sha256": "4e08808f3ea453c25570a5fec746899c7ad4051564c167e5fe3505a167abe0d8" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/completion-expired-final-config.toml", + "bytes": 2330, + "sha256": "4e9718c24a588f33b3d6d8d88ede1ec02c6bf8c8228303bfd057bd200299b143" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/completion-expiry-control.exit.txt", + "bytes": 2, + "sha256": "4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/completion-expiry-control.stderr.txt", + "bytes": 335, + "sha256": "5a4aad966dbd274a4dcc658af74a3b1a7adb53e7a635d01f081852aa71ea26ea" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/completion-expiry-control.stdout.json", + "bytes": 4265, + "sha256": "b34c8fcb7422d6bd507fb17bb4ca0da0bb982665d575ed005ebeb62da19367e1" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/completion-integration.exit.txt", + "bytes": 2, + "sha256": "9a271f2a916b0b6ee6cecb2426f0b3206ef074578be55d9bc94f6f3fe3ab86aa" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/completion-integration.stderr.txt", + "bytes": 28398, + "sha256": "ee349610c711adaa7a64da850fa7c8fd36a7447c8a3df2062acf446c4f119c21" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/completion-integration.stdout.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/completion-native-record.json", + "bytes": 8982, + "sha256": "140fe548dbeb2f9e3b520f40316c35ae8de4c698831ab23ceb390703bac34802" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/completion-osv-scanner-frozen-macos.sarif", + "bytes": 1168, + "sha256": "4568c81b00798436c2ec0a6ce67d41500aa483516d6b354f00bd1885c50b5955" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/completion-osv-scanner-frozen-wsl-retrieval.sarif", + "bytes": 1168, + "sha256": "4568c81b00798436c2ec0a6ce67d41500aa483516d6b354f00bd1885c50b5955" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/completion-osv-scanner.sarif", + "bytes": 1168, + "sha256": "4568c81b00798436c2ec0a6ce67d41500aa483516d6b354f00bd1885c50b5955" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/completion-scan.exit.txt", + "bytes": 2, + "sha256": "9a271f2a916b0b6ee6cecb2426f0b3206ef074578be55d9bc94f6f3fe3ab86aa" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/completion-scan.stderr.txt", + "bytes": 18215, + "sha256": "bb02db7b0614851f0847b822f0128259bab9afe37b68972499c3a9c603307209" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/completion-scan.stdout.txt", + "bytes": 18271, + "sha256": "4931d246717288ba22376c2c8a441134830c1a3849594cc6aac40bbf1c672d00" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/completion-scan.time.txt", + "bytes": 786, + "sha256": "d2d4bfd44026a121efa8ad7b722c2c1af1aad0e264b29ccafb927d1fc99fbe80" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/completion-validate-before-scan.exit.txt", + "bytes": 2, + "sha256": "4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/completion-validate-before-scan.stderr.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/completion-validate-before-scan.stdout.txt", + "bytes": 240, + "sha256": "cada4ef85786da5a8e8c4ae7486cac1cf47fc43f5ebf34c5fdc7356c34b6d36f" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/completion-workflow-scan.sh", + "bytes": 4308, + "sha256": "15ffbe6a824fb4bf041310488f17134adb79eadabda4a1407122d6f6a42c5d9e" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/controls.json", + "bytes": 12146, + "sha256": "d13f3f30f5f903305708a054af4b7f9f7d5b16d23baa0f2175dac571cd8ce48e" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/expired-archive-attempt2.stderr.txt", + "bytes": 319, + "sha256": "fbda52df16997854cca318d7b1c11500610161cfc6c5c8229ed17a157e7d492e" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/expired-archive-attempt2.stdout.json", + "bytes": 4238, + "sha256": "977a6e94151eadfc050710483f14683896a8b072585076f1a34844fd71845cfe" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/expired-archive.stderr.txt", + "bytes": 294, + "sha256": "d3f5e81d6d698b0ae93a28c9bfeec7098ed95ad8d719e7da64dc2c8afec42b2e" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/expired-archive.stdout.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/final-native-scan-record.json", + "bytes": 10898, + "sha256": "edddd3f4fdddc2d30b7c9c4dd3c9ba858ebe02d5fb86d6714ada547f1042fb97" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/final-scan-final-config-expired.toml", + "bytes": 2330, + "sha256": "a820c6eff5eed054a1d1b407e7fb186b1501bbf98b246fb0bb80fc846a5718da" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/final-scan-final-expired.stderr.txt", + "bytes": 347, + "sha256": "e45f599be0d3d0dd13a76c710466aeb34a308e2ba634286b1530b4cb50cdf821" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/final-scan-final-expired.stdout.json", + "bytes": 4268, + "sha256": "12e0886391e92655365a6e1dd67c99739fe4d1fa177207e98ef5e2a7adfabb12" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/final-scan-stderr.txt", + "bytes": 18363, + "sha256": "9063e604c63e35a3888e392a3cacc577bff8e7ad2ba65c221eabfdb5117f7a00" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/final-scan-stdout.txt", + "bytes": 18417, + "sha256": "15bd287f37de30aef9dd02294dcb3445160f288eac9572d809a506123e7d678a" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/final-scan-workflow-scan.sh", + "bytes": 4304, + "sha256": "f55f319bcfd2a1bf5fbbe19c1cd0b2155b75a8086b34586465b67c1db0d06625" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-controls-red-2.json", + "bytes": 13589, + "sha256": "134eb82d5e4ea09cbba659f55ab095b760b1ed7eff60678eb7ba30a8171f8a2b" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-controls-red.json", + "bytes": 6190, + "sha256": "16f39aa625e6b278a3a8e9866c61067308580aca768305c8fbee04ab1730a5b2" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-audit-corrected.json", + "bytes": 1147, + "sha256": "00fc95b6825f98ab647338b1d9a569e83932a05162d398880705a09b8065ece2" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-convergence-corrected.json", + "bytes": 1202, + "sha256": "37ac83973ce8964b38ab3d39015f87239fa6a30787b4bb00af01401754e18d54" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-convergence.json", + "bytes": 1007, + "sha256": "c02b9a8cb52f1b9260e9f226798a460e2ee3d35a20ab430a873e4b6851afb8a7" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-registry-validation.json", + "bytes": 1416, + "sha256": "b774599996dceabfcafef0b048762310b33a39a09a0ad10e7b78fbc25c23e7d8" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-final-tests-corrected.json", + "bytes": 5878, + "sha256": "2cfed079ea30283412fc8221bb1618342b0c48f8ff7571af9b9e9cabb74a16de" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/frozen-retrieval-source-runner-controls.json", + "bytes": 2429, + "sha256": "3d0d0533b74526b54b89e668c0e5bc53a0487425de5c087d74375b620d8b4ae6" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/independent-source-review.md", + "bytes": 2277, + "sha256": "690c99ab20795d8ac5dac47b25d524de4f597bce019e27f2bc68ad5dc232e614" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/integrated-osv-tests.stderr.txt", + "bytes": 7766, + "sha256": "266207212c8b17dfdd323d0ab321fcefd27ac03d5d70c6655ce1ceeccab80488" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/integrated-osv-tests.stdout.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/macos-portability-correction.md", + "sha256": "a5e818c83f779137dec59e0dc1d5d2742552742055de770519e554d15ce68f02", + "bytes": 5617 + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/macos-portability-integration.stdout.txt", + "sha256": "c6a0fc5f9311456adab5889d9dec5921c006fea6533071a90d1f7c4d33d20619", + "bytes": 276 + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-ignore.native-command.txt", + "bytes": 288, + "sha256": "1654f7d1ed9773f268e4f984aca1cd950b378690a1bdc20777b217a452754841" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-ignore.stderr.txt", + "bytes": 639, + "sha256": "0e2a6f0dfbc8166331bd4e4e62f03dd76aae4e938dc3916e4503b362279e9c24" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-ignore.stdout.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-prefix-ignore.native-command.txt", + "bytes": 325, + "sha256": "13cddf30b430b728b0eccd38a08b116ec87941bf851d7b84e3d087ab540b2a05" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-prefix-ignore.stderr.txt", + "bytes": 646, + "sha256": "4265db3c389d726228062ba7d1a91d10462aca40cf8d71e34a4345ece225d0ab" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-prefix-ignore.stdout.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-prefix.native-command.txt", + "bytes": 299, + "sha256": "00613e888df6ccca4c05e3ddfedbf070b1b70a1ced962efe3f3469fb8356809a" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-prefix.stderr.txt", + "bytes": 639, + "sha256": "417b69b6d42f8960cd37baaaebeb48c28fececb9c72d289aad15cb20a192b0a3" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci-prefix.stdout.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci.native-command.txt", + "bytes": 262, + "sha256": "78e902a357299fdc7b2a79b7512084ca83fc2c112cd69e7e4e2da1a85513cb7e" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci.stderr.txt", + "bytes": 632, + "sha256": "12dd3fb22d309e147f2b5e5fcb06040c645487b6996c846925bda70b52247852" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/npm-ci.stdout.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-ignore.native-command.txt", + "bytes": 298, + "sha256": "351569d470470479f317374dc31ce440bf20ec97f80a8e4db9465fc0fe01891c" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-ignore.stderr.txt", + "bytes": 644, + "sha256": "3df09a18c83259eb852943e8801ded30e3d070adb19f96df0715e6ca03160682" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-ignore.stdout.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-prefix-ignore.native-command.txt", + "bytes": 335, + "sha256": "714336d9bd268dec7e70329219ee4483ebbf7272cfffaacba20e03083fc366a5" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-prefix-ignore.stderr.txt", + "bytes": 651, + "sha256": "46b7816e3ccca428816bb99ab777f86b098c11359890d4d89c148276050b7ed5" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-prefix-ignore.stdout.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-prefix.native-command.txt", + "bytes": 309, + "sha256": "a3f6d02228d6d8f7b705410c0c7f16ee3a67dbbd83e74bf44cc63c1e7151b5e8" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-prefix.stderr.txt", + "bytes": 644, + "sha256": "db82ddd782c6966f9546348d032ae3c5311ee8e53f15f04660673c14f4c2f5e9" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install-prefix.stdout.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install.native-command.txt", + "bytes": 272, + "sha256": "591050f725b226fceacb6dd41a607fe2465def6c0432edc08051e92e1894229f" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install.stderr.txt", + "bytes": 637, + "sha256": "813228f309860963e373f19fe5dce00ebee8aa83e7d406ccd0e63bd233524e94" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/npm-install.stdout.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/ordinary-active.stderr.txt", + "bytes": 382, + "sha256": "910710222f50d1e2e0436a5ae85c7eabaa69cfcac730e1047454f4e0ad10a971" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/ordinary-active.stdout.json", + "bytes": 4238, + "sha256": "977a6e94151eadfc050710483f14683896a8b072585076f1a34844fd71845cfe" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/osv-scanner-frozen-macos.sarif", + "bytes": 1168, + "sha256": "4568c81b00798436c2ec0a6ce67d41500aa483516d6b354f00bd1885c50b5955" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/osv-scanner-frozen-wsl-retrieval.sarif", + "bytes": 1168, + "sha256": "4568c81b00798436c2ec0a6ce67d41500aa483516d6b354f00bd1885c50b5955" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/osv-scanner.sarif", + "bytes": 1168, + "sha256": "4568c81b00798436c2ec0a6ce67d41500aa483516d6b354f00bd1885c50b5955" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/prospective-archive-attempt2.stderr.txt", + "bytes": 968, + "sha256": "500d6eb187a05311b37d0cb6d03ea2752b365cf869d44fb98bfc37930d876b11" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/prospective-archive-attempt2.stdout.json", + "bytes": 124, + "sha256": "54345d5194d238b6017d0f3181f392109ce35023cc6b4bdfa8c134943dcb578e" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/prospective-archive.stderr.txt", + "bytes": 294, + "sha256": "51d1d7bf8dd549a7e1e7fec51000cb7010e626455904fc852033c6d05f64d26f" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/prospective-archive.stdout.txt", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/publication-correction.md", + "bytes": 1671, + "sha256": "b833498956e275154205a4ce17fda3d47b0c7155001207230de1429e2dddc2a5" + }, + { + "path": "evidence/artifacts/wsl-retrieval-retirement-20261003/receipt-binding-correction.md", + "sha256": "91a3be1712b4ab7e495e8420fbc05611351c7811faf45ccef14d2f8bf329a6a6", + "bytes": 2896 + }, { "path": "evidence/artifacts/wsl-terminal-defaults-20260929/README.md", "sha256": "4416d422a5ec3064dd14f7c27a9b09191a5568f1b7c8bade742f0bc351089d27", @@ -47989,6 +48464,11 @@ "sha256": "02fc96b030e8e8d48c977809a80f1a5d0409523cffb12a79aa0c9f4a9de8a56c", "bytes": 2455 }, + { + "path": "evidence/receipts/wsl-retrieval-retirement-20261003.json", + "sha256": "332fe3c68b7a376caaf72af1976fff213fd604702b326ae85586c7d6c602a91b", + "bytes": 6608 + }, { "path": "evidence/receipts/wsl-terminal-defaults-20260929.json", "sha256": "5747e6c7323ed3ed94aca51dce6984e823b8950c8a2854576b1627280744dd33", @@ -49641,8 +50121,8 @@ }, { "path": "tests/test_osv_lockfile_coverage.py", - "sha256": "31780dc60ea10f55597dbc984628fa22019a0a0c3cc63727f2a684fff2ea383e", - "bytes": 46337 + "sha256": "a5cb994e66138fdad5da76abaaf6e4645266b575eaf722cfb239cc78fe6d6da9", + "bytes": 65234 }, { "path": "tests/test_path_safety.py", @@ -49886,8 +50366,8 @@ }, { "path": "tests/test_workflow_hardening.py", - "sha256": "f9b5b418ac5bd232aaad49f20bec2d2cdef556e31c8fb87397a4ad0ede0e21b3", - "bytes": 87393 + "sha256": "44392072d04e137e45c8d92f99b9f7ce650e75c39446731b016e3150a4c9424d", + "bytes": 87539 }, { "path": "tests/test_workflow_security.py", @@ -49921,8 +50401,8 @@ }, { "path": "tests/test_wsl_retrieval.py", - "sha256": "83353f3ccaa8bc01518f529846bf92167f7e21023e430ef8523af3e129afd903", - "bytes": 12720 + "sha256": "b4e1abcc70ed75d302ca3ecabfba23bda9f53ab4a460a1d0fc16fd47453ea7fa", + "bytes": 18897 }, { "path": "tests/test_zizmor_negative_control.py", diff --git a/tests/test_osv_lockfile_coverage.py b/tests/test_osv_lockfile_coverage.py index 57473c234..f1352c664 100644 --- a/tests/test_osv_lockfile_coverage.py +++ b/tests/test_osv_lockfile_coverage.py @@ -8,8 +8,8 @@ for that lock and advisory at the lock's reviewed sha256. That guard follows includes and fails closed on a version or line it cannot parse strictly; an allowed lock must be self-contained, and an ignore counts as active only before its ignoreUntil date. A dated exception for one frozen artifact lives in a config of its own -that only that lock's scan uses (FROZEN_LOCKS): the inventory split and that config's scope are checked here, and the scanner itself -keeps the exception from reaching any other lock. +that only that lock's scan uses (FROZEN_LOCKS). The workflow and these policy checks enforce the split and scope; +OSV applies an explicit config to every invocation input and does not enforce that path boundary itself. """ from datetime import date, datetime, timedelta, timezone @@ -20,13 +20,18 @@ import re import subprocess import tempfile +import textwrap import tomllib import unittest +import sys +from unittest.mock import patch ROOT = Path(__file__).resolve().parents[1] INVENTORY = ROOT / ".github/osv-scanner-lockfiles.json" CONFIG = ROOT / ".github/osv-scanner.toml" FROZEN_CONFIG = ".github/osv-scanner-frozen-macos.toml" +FROZEN_WSL_CONFIG = ".github/osv-scanner-frozen-wsl-retrieval.toml" +WSL_LOCK = "blueprints/convergence-practice/wsl-retrieval/package-lock.json" WORKFLOW = ROOT / ".github/workflows/security-scan.yml" # Dependency lockfile and manifest names in this repository or supported by OSV-Scanner v2's # source extractors (docs/supported_languages_and_lockfiles.md at v2.6.0). @@ -45,9 +50,9 @@ PARSERS = {"requirements.txt", "packages.lock.json"} -# osv-scanner 2.6.0 matches [[IgnoredVulns]] by id in every lockfile it scans (ShouldIgnore checks only the id and -# ignoreUntil, and security-scan.yml passes one --config for the whole inventory). An ignore added for one lock is -# therefore repo-wide. IGNORE_SCOPES names the package versions each such ignore affects. IGNORE_ALLOWED_LOCKS names the +# osv-scanner 2.6.0 matches [[IgnoredVulns]] by id in every input of that invocation (ShouldIgnore checks the id and +# ignoreUntil). An ignore in CONFIG therefore reaches the whole ordinary group; dedicated archive groups have separate +# configs. IGNORE_SCOPES names the package versions each ordinary ignore affects. IGNORE_ALLOWED_LOCKS names the # only locks allowed to pin them: per inventory lockfile, the advisories whose non-reachability was reviewed for it, the # sha256 of the lock content that review covered and the repository path of its evidence. A changed lock needs a new # review before its new digest is recorded here, and an allowed lock must be self-contained: its digest covers only its @@ -90,6 +95,13 @@ "sha256": "f1c707b8295e85bd396e49b990de92dc82bc0d58eca1e4e4bef31262d9898cd2", "evidence": "evidence/receipts/osv-urllib3-next-20260930.json", }, + WSL_LOCK: { + "config": FROZEN_WSL_CONFIG, + "advisories": ["GHSA-vfj7-8cjw-p6xm"], + "sha256": "5c51ee65cc477f2c1488a38ff5cad1c0a737f81a5b61bbd70d5edc4d15bfc3bb", + "evidence": "evidence/receipts/wsl-retrieval-retirement-20261003.json", + "retirement_assessment": "blueprints/convergence-practice/wsl-retrieval/retirement-assessment.json", + }, } # Requirements files follow pip's format (https://pip.pypa.io/en/stable/reference/requirements-file-format/): a line @@ -279,7 +291,7 @@ def scan_partition(entries, configs=None): def ignore_entry_problems(config): - """Why an [[IgnoredVulns]] entry of a parsed config breaks the ignore policy (a missing id or reason, a missing or distant ignoreUntil).""" + """Why a config ignore breaks the policy: missing identity/reason, invalid or distant date, or expired grant.""" problems, latest = [], date.today() + timedelta(days=90) for entry in config.get("IgnoredVulns", []): if not entry.get("id"): @@ -294,6 +306,8 @@ def ignore_entry_problems(config): until = until.date() if until > latest: problems.append(f"{entry.get('id')}: ignoreUntil more than 90 days away") + if until <= date.today(): + problems.append(f"{entry.get('id')}: ignoreUntil has expired") return problems @@ -521,8 +535,7 @@ def test_no_other_suppression_mechanism_bypasses_the_policy(self): class FrozenScanTests(unittest.TestCase): - """A dated exception for a frozen artifact is scoped by the scanner, not by a reader of the lock: its config is used only by the scan of the - inventory entries that name it (docs/decisions/2026-09-22-github-automation-closure.md, "urllib3 and PyJWT relock and a frozen macOS lock").""" + """The workflow and policy preflight bind each explicit OSV config to its reviewed artifact; OSV itself applies it to all inputs.""" inventory = json.loads(INVENTORY.read_text(encoding="utf-8")) ordinary_config = tomllib.loads(CONFIG.read_text(encoding="utf-8")) @@ -549,7 +562,7 @@ def test_the_ordinary_config_has_no_exception_for_a_frozen_advisory(self): ids = {entry["id"] for entry in self.ordinary_config.get("IgnoredVulns", [])} frozen_ids = {advisory for lock in FROZEN_LOCKS.values() for advisory in lock["advisories"]} self.assertEqual(ids & frozen_ids, set(), "the exception belongs in the frozen config, which only the frozen scan uses") - self.assertEqual({override.get("name") for override in self.ordinary_config.get("PackageOverrides", [])} & {"next"}, set()) + self.assertEqual({override.get("name") for override in self.ordinary_config.get("PackageOverrides", [])} & {"next", "braces"}, set()) def test_each_frozen_config_holds_exactly_the_advisories_of_its_locks(self): for config_path in sorted({lock["config"] for lock in FROZEN_LOCKS.values()}): @@ -558,23 +571,39 @@ def test_each_frozen_config_holds_exactly_the_advisories_of_its_locks(self): expected = sorted(advisory for lock in FROZEN_LOCKS.values() if lock["config"] == config_path for advisory in lock["advisories"]) self.assertEqual(sorted(entry["id"] for entry in config["IgnoredVulns"]), expected) self.assertEqual(ignore_entry_problems(config), []) + self.assertEqual(active_ignores(config), set(expected), 'an archive exception must still be active (UTC)') def test_each_frozen_lock_matches_its_reviewed_digest_and_names_evidence(self): for path, lock in FROZEN_LOCKS.items(): + self.assertTrue((ROOT / path).is_file(), path) self.assertEqual(hashlib.sha256((ROOT / path).read_bytes()).hexdigest(), lock["sha256"], f"{path} changed after its review: re-review whether it reaches the advisories of its config, then record the new sha256") self.assertTrue((ROOT / lock["evidence"]).is_file(), lock["evidence"]) + if "retirement_assessment" in lock: + assessment_path = ROOT / lock["retirement_assessment"] + self.assertTrue(assessment_path.is_file(), lock["retirement_assessment"]) + assessment = json.loads(assessment_path.read_text(encoding="utf-8")) + self.assertEqual(assessment["status"], "retired_historical_source", path) + self.assertEqual(assessment["retained_lock"]["path"], Path(path).name, path) + self.assertEqual(assessment["retained_lock"]["sha256"], lock["sha256"], path) + receipt = json.loads((ROOT / lock["evidence"]).read_text(encoding="utf-8")) + self.assertEqual(receipt["id"], "wsl-retrieval-retirement-20261003", lock["evidence"]) + self.assertEqual(receipt["retirement_assessment"], lock["retirement_assessment"]) + self.assertIn(path, receipt["evidence_paths"], lock["evidence"]) def test_the_workflow_scans_each_config_in_its_own_invocation(self): text = self.workflow configs = {lock["config"] for lock in FROZEN_LOCKS.values()} - self.assertEqual(re.findall(r"(?m)^\s+frozen_config=(\S+)$", text), sorted(configs)) + self.assertEqual(re.findall(r"(?m)^\s+frozen(?:_wsl)?_config=(\S+)$", text), sorted(configs)) for needle in ('select(has("config") | not)', 'select(.config == $config)', "--config .github/osv-scanner.toml", '--config "$frozen_config"', - '$(( ${#lockfiles[@]} + ${#frozen[@]} ))', 'jq \'.lockfiles | length\' "$inventory"', "osv-scanner-frozen-macos.sarif"): + '$(( ${#lockfiles[@]} + ${#frozen[@]} + ${#frozen_wsl[@]} ))', + 'jq \'.lockfiles | length\' "$inventory"', "osv-scanner-frozen-macos.sarif", + '--config "$frozen_wsl_config"', 'osv-scanner-frozen-wsl-retrieval.sarif'): self.assertIn(needle, text, needle) # the frozen scan never gets the ordinary lock list, and the ordinary scan never gets the frozen one self.assertIn('"${frozen[@]}")', text) self.assertEqual(text.count('"${lockfiles[@]}")'), 1) + self.assertEqual(text.count('"${frozen_wsl[@]}")'), 1) def test_the_partition_check_catches_a_mutant_inventory(self): entries = [{"path": "a"}, {"path": "b", "config": FROZEN_CONFIG}, {"path": "c", "config": ".github/other.toml"}, {"path": "d", "parser": "requirements.txt"}] @@ -584,6 +613,294 @@ def test_the_partition_check_catches_a_mutant_inventory(self): self.assertEqual([entry["path"] for entry in stray], ["c"]) self.assertEqual(len(ordinary) + sum(len(group) for group in frozen.values()) + len(stray), len(entries)) + def test_wsl_lock_has_a_dedicated_config_and_fixed_identity(self): + entry = next(entry for entry in self.inventory['lockfiles'] if entry['path'] == WSL_LOCK) + self.assertEqual(entry.get('config'), FROZEN_WSL_CONFIG) + grant = FROZEN_LOCKS[WSL_LOCK] + self.assertEqual(grant['config'], FROZEN_WSL_CONFIG) + self.assertEqual(grant['advisories'], ['GHSA-vfj7-8cjw-p6xm']) + self.assertEqual(grant['sha256'], '5c51ee65cc477f2c1488a38ff5cad1c0a737f81a5b61bbd70d5edc4d15bfc3bb') + self.assertEqual(grant['evidence'], 'evidence/receipts/wsl-retrieval-retirement-20261003.json') + + def test_wsl_config_is_short_lived_and_names_its_evidence(self): + path = ROOT / FROZEN_WSL_CONFIG + self.assertTrue(path.is_file(), 'retired WSL lock needs a dedicated config') + config = tomllib.loads(path.read_text(encoding='utf-8')) + self.assertEqual(set(config), {'IgnoredVulns'}) + self.assertEqual(len(config['IgnoredVulns']), 1) + entry = config['IgnoredVulns'][0] + self.assertEqual(entry['id'], 'GHSA-vfj7-8cjw-p6xm') + self.assertLessEqual(entry['ignoreUntil'], date(2026, 10, 17)) + for binding in [WSL_LOCK, FROZEN_LOCKS[WSL_LOCK]['sha256'], FROZEN_LOCKS[WSL_LOCK]['evidence'], + 'blueprints/convergence-practice/wsl-retrieval/retirement-assessment.json']: + self.assertIn(binding, entry['reason']) + + def test_expired_ignore_fails_the_policy_before_a_scan(self): + for until in [date(2000, 1, 1), date.today()]: + with self.subTest(until=until): + config = {'IgnoredVulns': [{'id': 'GHSA-vfj7-8cjw-p6xm', 'reason': 'dated retirement evidence', + 'ignoreUntil': until}]} + self.assertTrue(any('expired' in message for message in ignore_entry_problems(config))) + + +class SyntheticWorkflowInvocationTests(unittest.TestCase): + """Execute the workflow shell with recording doubles; check routing/status behavior, never native scanner acceptance.""" + + configs = ['.github/osv-scanner.toml', FROZEN_CONFIG, FROZEN_WSL_CONFIG] + + def run_step(self, inventory=None, primary=(0, 0, 0), sarif=(0, 0, 0), write_sarif=True, preflight=0): + workflow = WORKFLOW.read_text(encoding='utf-8') + step = workflow.split(' - name: Scan every listed lockfile and manifest', 1)[1] + body = textwrap.dedent(step.split(' run: |\n', 1)[1].split(' - name:', 1)[0]) + inventory = inventory or json.loads(INVENTORY.read_text(encoding='utf-8')) + with tempfile.TemporaryDirectory() as directory: + scratch = Path(directory) + (scratch / '.github').mkdir() + (scratch / '.github/osv-scanner-lockfiles.json').write_text(json.dumps(inventory), encoding='utf-8') + binaries = scratch / 'bin' + binaries.mkdir() + tool_directory = scratch / 'runner/osv-scanner' + tool_directory.mkdir(parents=True) + double = '#!' + sys.executable + '\n' + textwrap.dedent('''\ + import json, os, sys + from pathlib import Path + argv = sys.argv[1:] + preflight = Path(sys.argv[0]).name == 'python3' + fact = {'kind': 'preflight' if preflight else 'scanner', 'argv': argv} + with open(os.environ['OSV_WORKFLOW_LOG'], 'a', encoding='utf-8') as output: + output.write(json.dumps(fact) + '\\n') + if preflight: + raise SystemExit(int(os.environ['OSV_WORKFLOW_PREFLIGHT'])) + config = argv[argv.index('--config') + 1] + format_run = '--format' in argv + if format_run: + Path(argv[argv.index('--output-file') + 1]).write_text('synthetic workflow fixture; not native SARIF\\n') + status = json.loads(os.environ['OSV_WORKFLOW_STATUSES'])[config]['sarif' if format_run else 'primary'] + raise SystemExit(status) + ''') + for path in [binaries / 'python3', tool_directory / 'osv-scanner']: + path.write_text(double, encoding='utf-8') + path.chmod(0o700) + environment = dict(os.environ, PATH=str(binaries) + os.pathsep + os.environ['PATH'], + RUNNER_TEMP=str(scratch / 'runner'), WRITE_SARIF=str(write_sarif).lower(), + OSV_WORKFLOW_LOG=str(scratch / 'calls.jsonl'), OSV_WORKFLOW_PREFLIGHT=str(preflight), + OSV_WORKFLOW_STATUSES=json.dumps({config: {'primary': p, 'sarif': s} + for config, p, s in zip(self.configs, primary, sarif)})) + result = subprocess.run(['bash', '-c', body], cwd=scratch, env=environment, + text=True, capture_output=True, check=False) + log = scratch / 'calls.jsonl' + calls = [json.loads(line) for line in log.read_text().splitlines()] if log.exists() else [] + artifacts = sorted(path.name for path in tool_directory.glob('*.sarif')) + return result, calls, artifacts + + def test_three_invocations_keep_every_input_and_parser_separate(self): + result, calls, artifacts = self.run_step() + self.assertEqual(result.returncode, 0, result.stderr) + preflight = [call for call in calls if call['kind'] == 'preflight'] + self.assertEqual(len(preflight), 1) + self.assertIn('tests.test_osv_lockfile_coverage.LockfileInventoryTests', preflight[0]['argv']) + self.assertIn('tests.test_osv_lockfile_coverage.FrozenScanTests', preflight[0]['argv']) + self.assertIn('tests.test_wsl_retrieval.RetiredRunnerTests', preflight[0]['argv']) + scanners = [call['argv'] for call in calls if call['kind'] == 'scanner'] + self.assertEqual(len(scanners), 6) + inventory = json.loads(INVENTORY.read_text(encoding='utf-8')) + for config in self.configs: + group = [argv for argv in scanners if argv[argv.index('--config') + 1] == config] + self.assertEqual(len(group), 2, config) + expected = ['--lockfile=' + entry.get('parser', '') + ':' + entry['path'] + for entry in inventory['lockfiles'] + if entry.get('config', '.github/osv-scanner.toml') == config] + self.assertGreater(len(expected), 0) + for argv in group: + self.assertEqual(argv[:2], ['scan', 'source']) + self.assertIn('--no-resolve', argv) + self.assertEqual([arg for arg in argv if arg.startswith('--lockfile=')], expected) + self.assertEqual(artifacts, ['osv-scanner-frozen-macos.sarif', 'osv-scanner-frozen-wsl-retrieval.sarif', + 'osv-scanner.sarif']) + + def test_each_primary_and_sarif_status_is_retained(self): + cases = [((1, 0, 0), (0, 0, 0), 1), ((0, 1, 0), (0, 0, 0), 1), + ((0, 0, 1), (0, 0, 0), 1), ((2, 0, 0), (0, 0, 0), 2), + ((0, 0, 0), (0, 0, 1), 1), ((0, 0, 0), (0, 0, 7), 7), + ((0, 7, 0), (2, 0, 0), 7), ((0, 0, 127), (0, 0, 0), 127)] + for primary, sarif, expected in cases: + with self.subTest(primary=primary, sarif=sarif): + result, calls, artifacts = self.run_step(primary=primary, sarif=sarif) + self.assertEqual(result.returncode, expected, result.stderr) + self.assertEqual(len([call for call in calls if call['kind'] == 'scanner']), 6) + self.assertEqual(len(artifacts), 3) + + def test_pr_collects_three_primary_statuses_without_sarif(self): + result, calls, artifacts = self.run_step(primary=(0, 0, 1), write_sarif=False) + self.assertEqual(result.returncode, 1, result.stderr) + self.assertEqual(len([call for call in calls if call['kind'] == 'scanner']), 3) + self.assertEqual(artifacts, []) + + def test_unknown_duplicate_missing_and_wrong_archive_assignments_fail_before_scan(self): + for mutation in ['unknown-config', 'duplicate', 'missing-wsl', 'wrong-wsl-lock']: + with self.subTest(mutation=mutation): + inventory = json.loads(INVENTORY.read_text(encoding='utf-8')) + entries = inventory['lockfiles'] + if mutation == 'unknown-config': + entries[0]['config'] = '.github/unknown.toml' + elif mutation == 'duplicate': + entries.append(dict(entries[0])) + elif mutation == 'missing-wsl': + inventory['lockfiles'] = [entry for entry in entries if entry['path'] != WSL_LOCK] + else: + wsl = next(entry for entry in entries if entry['path'] == WSL_LOCK) + wsl['path'] = 'other-active-project/package-lock.json' + result, calls, artifacts = self.run_step(inventory=inventory) + self.assertEqual(result.returncode, 1, result.stderr) + self.assertEqual([call for call in calls if call['kind'] == 'scanner'], []) + self.assertEqual(artifacts, []) + + def test_preflight_failure_is_returned_before_scan(self): + for status in [1, 7, 127]: + with self.subTest(status=status): + result, calls, artifacts = self.run_step(preflight=status) + self.assertEqual(result.returncode, status, result.stderr) + self.assertEqual(len(calls), 1) + self.assertEqual(calls[0]['kind'], 'preflight') + self.assertEqual(artifacts, []) + + +class FrozenPolicyMutationTests(unittest.TestCase): + """Mutate candidate policy inputs and exercise the actual guards the workflow invokes. + + Scratch evidence is explicitly synthetic; these controls prove policy rejection, + never archive eligibility or native scanner acceptance. + """ + + def setUp(self): + temporary = tempfile.TemporaryDirectory() + self.addCleanup(temporary.cleanup) + self.scratch = Path(temporary.name) + for path, grant in FROZEN_LOCKS.items(): + for name in (path, grant['config']): + target = self.scratch / name + target.parent.mkdir(parents=True, exist_ok=True) + target.write_bytes((ROOT / name).read_bytes()) + evidence = self.scratch / grant['evidence'] + evidence.parent.mkdir(parents=True, exist_ok=True) + evidence.write_text('{"synthetic_policy_fixture": true}\n', encoding='utf-8') + grant = FROZEN_LOCKS[WSL_LOCK] + self.assessment = self.scratch / grant['retirement_assessment'] + self.assessment.write_text(json.dumps({ + 'synthetic_policy_fixture': True, + 'status': 'retired_historical_source', + 'retained_lock': {'path': 'package-lock.json', 'sha256': grant['sha256']}, + }), encoding='utf-8') + self.receipt = self.scratch / grant['evidence'] + self.receipt.write_text(json.dumps({ + 'synthetic_policy_fixture': True, + 'id': 'wsl-retrieval-retirement-20261003', + 'retirement_assessment': grant['retirement_assessment'], + 'evidence_paths': [WSL_LOCK, grant['retirement_assessment']], + }), encoding='utf-8') + + def check(self, method, **attributes): + case = FrozenScanTests(method) + for key, value in attributes.items(): + setattr(case, key, value) + result = unittest.TestResult() + with patch(__name__ + '.ROOT', self.scratch): + case.run(result) + return result + + def assert_rejected(self, method, **attributes): + result = self.check(method, **attributes) + self.assertTrue(result.failures, 'the actual preflight guard accepted the changed input') + self.assertEqual(result.errors, [], result.errors) + + def test_changed_lock_bytes_and_missing_evidence_are_rejected(self): + method = 'test_each_frozen_lock_matches_its_reviewed_digest_and_names_evidence' + self.assertTrue(self.check(method).wasSuccessful()) + lock = self.scratch / WSL_LOCK + original = lock.read_bytes() + lock.write_bytes(original + b'\n') + self.assert_rejected(method) + lock.write_bytes(original) + self.receipt.unlink() + self.assert_rejected(method) + + def test_changed_retirement_status_and_assessment_lock_are_rejected(self): + method = 'test_each_frozen_lock_matches_its_reviewed_digest_and_names_evidence' + original = self.assessment.read_text(encoding='utf-8') + for mutation in ('restored-status', 'wrong-digest', 'wrong-path', 'missing-assessment'): + with self.subTest(mutation=mutation): + data = json.loads(original) + if mutation == 'restored-status': + data['status'] = 'active_replay' + elif mutation == 'wrong-digest': + data['retained_lock']['sha256'] = '0' * 64 + elif mutation == 'wrong-path': + data['retained_lock']['path'] = 'another-lock.json' + self.assessment.write_text(json.dumps(data), encoding='utf-8') + if mutation == 'missing-assessment': + self.assessment.unlink() + self.assert_rejected(method) + self.assessment.write_text(original, encoding='utf-8') + + def test_wrong_receipt_identity_or_artifact_binding_is_rejected(self): + method = 'test_each_frozen_lock_matches_its_reviewed_digest_and_names_evidence' + original = self.receipt.read_text(encoding='utf-8') + for mutation in ('wrong-id', 'wrong-assessment', 'missing-lock'): + with self.subTest(mutation=mutation): + data = json.loads(original) + if mutation == 'wrong-id': + data['id'] = 'another-receipt' + elif mutation == 'wrong-assessment': + data['retirement_assessment'] = 'another-assessment.json' + else: + data['evidence_paths'].remove(WSL_LOCK) + self.receipt.write_text(json.dumps(data), encoding='utf-8') + self.assert_rejected(method) + + def test_frozen_advisory_or_package_override_cannot_leak_to_ordinary_inputs(self): + original = FrozenScanTests.ordinary_config + for mutation in ('advisory', 'package-override'): + with self.subTest(mutation=mutation): + config = {key: list(value) for key, value in original.items()} + if mutation == 'advisory': + config.setdefault('IgnoredVulns', []).append({'id': 'GHSA-vfj7-8cjw-p6xm'}) + else: + config.setdefault('PackageOverrides', []).append({'name': 'braces', 'ignore': True}) + self.assert_rejected('test_the_ordinary_config_has_no_exception_for_a_frozen_advisory', + ordinary_config=config) + + def test_wsl_advisory_cannot_leak_into_the_macos_config(self): + config = self.scratch / FROZEN_CONFIG + with config.open('a', encoding='utf-8') as output: + output.write('\n[[IgnoredVulns]]\nid = "GHSA-vfj7-8cjw-p6xm"\n' + 'ignoreUntil = 2026-10-17\nreason = "synthetic leaked exception"\n') + self.assert_rejected('test_each_frozen_config_holds_exactly_the_advisories_of_its_locks') + + def test_missing_reason_binding_or_extended_expiry_is_rejected(self): + config = self.scratch / FROZEN_WSL_CONFIG + original = config.read_text(encoding='utf-8') + for mutation in ('reason', 'expiry'): + with self.subTest(mutation=mutation): + changed = (re.sub(r'(?m)^reason = .*$', 'reason = "unbound exception"', original) + if mutation == 'reason' else original.replace('2026-10-17', '2026-10-18')) + config.write_text(changed, encoding='utf-8') + self.assert_rejected('test_wsl_config_is_short_lived_and_names_its_evidence') + + def test_expired_config_is_rejected_by_the_actual_preflight_guard(self): + config = self.scratch / FROZEN_WSL_CONFIG + config.write_text(config.read_text(encoding='utf-8').replace('2026-10-17', '2000-01-01'), encoding='utf-8') + self.assert_rejected('test_each_frozen_config_holds_exactly_the_advisories_of_its_locks') + + def test_ordinary_inventory_omission_is_rejected_by_the_actual_preflight_guard(self): + inventory = json.loads(INVENTORY.read_text(encoding='utf-8')) + inventory['lockfiles'].remove(next(entry for entry in inventory['lockfiles'] if 'config' not in entry)) + case = LockfileInventoryTests('test_every_tracked_lockfile_and_manifest_is_listed') + case.inventory = inventory + result = unittest.TestResult() + case.run(result) + self.assertTrue(result.failures, 'preflight accepted an omitted tracked active input') + self.assertEqual(result.errors, [], result.errors) + class AllowedLockTests(unittest.TestCase): """Each IGNORE_ALLOWED_LOCKS entry is a scanned lock, bound to the advisories reviewed for it, the sha256 of the diff --git a/tests/test_workflow_hardening.py b/tests/test_workflow_hardening.py index 5a4a2e2b6..205e007c2 100644 --- a/tests/test_workflow_hardening.py +++ b/tests/test_workflow_hardening.py @@ -274,8 +274,8 @@ def test_write_scope_is_job_local_and_limited_to_security_events(self): def test_the_write_token_never_reaches_an_installed_tool(self): self.assertIn("GH_TOKEN: ${{ github.token }}", jobs(self.text)["zizmor-online"]) - # osv-sarif-upload sends two reports (the ordinary scan and the frozen-artifact scan), each under its own category. - for tool_job, upload_job, uploads in (("osv-scanner", "osv-sarif-upload", 2), ("zizmor-online", "zizmor-sarif-upload", 1)): + # OSV uploads the ordinary, frozen macOS and retired WSL reports under separate categories. + for tool_job, upload_job, uploads in (("osv-scanner", "osv-sarif-upload", 3), ("zizmor-online", "zizmor-sarif-upload", 1)): upload = jobs(self.text)[upload_job] self.assertIn(f"needs: {tool_job}", upload, upload_job) self.assertNotRegex(upload, r"(?m)^\s+(- )?run:", f"{upload_job} (write scope) runs no shell step") @@ -298,11 +298,13 @@ def test_osv_scanner_fails_on_findings_and_uploads_sarif_off_pull_requests(self) self.assertIn(UPLOAD_SARIF, upload) self.assertIn("category: osv-scanner\n", upload) self.assertIn("category: osv-scanner-frozen-macos", upload) - # Both native scans write a report, and the step fails on the worse of their two statuses. - for report in ("osv-scanner.sarif", "osv-scanner-frozen-macos.sarif"): + self.assertIn("category: osv-scanner-frozen-wsl-retrieval", upload) + # Every group writes a report; all primary and SARIF statuses participate in the final status. + for report in ("osv-scanner.sarif", "osv-scanner-frozen-macos.sarif", "osv-scanner-frozen-wsl-retrieval.sarif"): self.assertIn(report, job) self.assertIn(report, upload) self.assertIn("frozen_status", job) + self.assertIn("frozen_wsl_status", job) def test_zizmor_online_skips_pull_requests_and_reports_without_failing(self): job = jobs(self.text)["zizmor-online"] diff --git a/tests/test_wsl_retrieval.py b/tests/test_wsl_retrieval.py index 2e83f8367..552df5c0c 100644 --- a/tests/test_wsl_retrieval.py +++ b/tests/test_wsl_retrieval.py @@ -1,11 +1,15 @@ """Offline regressions for selected WSL native retrieval evidence.""" import hashlib import importlib.util +from importlib.machinery import SourceFileLoader +import io import json import shutil import subprocess +import sys import tempfile import unittest +from contextlib import redirect_stderr, redirect_stdout from pathlib import Path from unittest.mock import patch @@ -17,6 +21,10 @@ RUN_SPEC = importlib.util.spec_from_file_location("wsl_retrieval_runner", ROOT/"run.py") RUNNER = importlib.util.module_from_spec(RUN_SPEC) RUN_SPEC.loader.exec_module(RUNNER) +RECORDING_SPEC = importlib.util.spec_from_loader('wsl_retrieval_recording_archive', + SourceFileLoader('wsl_retrieval_recording_archive', str(ROOT/'run-recording-aid.py.txt'))) +RECORDING_ARCHIVE = importlib.util.module_from_spec(RECORDING_SPEC) +RECORDING_SPEC.loader.exec_module(RECORDING_ARCHIVE) class WslRetrievalEvidenceTests(unittest.TestCase): @@ -83,6 +91,11 @@ def test_archived_runner_and_native_receipts_remain_original_bytes(self): 'qmd-attempt-1.json': '452901227a56f61229b249a56c8b4233faeb3763058610856d399149f81655e6', 'qmd-receipt.json': 'cad5b3dc802323e5aad8fcdb4ca98f7d1c3e92749b8e891dc32149c70d3f9527', 'install-receipt.json': '8a3f343e92aa6b0b4ce814ed1b21b56851a37e26f88488cc5ae1119f83063654', + 'package-original.json.txt': '7bbf63c5eafd347ae5ae56c684be06ef2589d38f2aab580ca7986ca4122bc6a8', + 'run-recording-aid.py.txt': 'be852ce99501f5bc4567b846b90fb0e91d91de77b0eafbd3b72bb4484a2f7d12', + 'package-lock.json': '5c51ee65cc477f2c1488a38ff5cad1c0a737f81a5b61bbd70d5edc4d15bfc3bb', + 'source-review.json': '9ecd2bf01d7c19248b848dcbd0b77f286b4f1aee775068d7778fabf37721d423', + 'verification.json': '1cb605f4574a662272194625d75689ab5cdcae3e3dbb3ba35afbbd827a474cbe', } for name, digest in originals.items(): with self.subTest(artifact=name): @@ -197,6 +210,27 @@ def test_changed_frozen_input_rejected(self): with self.assertRaises(ValueError): self.audit(target) + def test_original_manifest_is_resolved_without_rewriting_receipt_names(self): + with tempfile.TemporaryDirectory() as folder: + target = Path(folder)/'leaf' + shutil.copytree(ROOT, target) + archive = target/'package-original.json.txt' + (target/'package.json').write_text('{"private": true}\n') + try: + result = self.audit(target) + except ValueError as exc: + self.fail('historical manifest must resolve to its preserved archive: '+str(exc)) + self.assertTrue(result['evidence_consistent']) + + def test_changed_original_manifest_archive_rejected(self): + with tempfile.TemporaryDirectory() as folder: + target = Path(folder)/'leaf' + shutil.copytree(ROOT, target) + archive = target/'package-original.json.txt' + archive.write_bytes(archive.read_bytes()+b'\n') + with self.assertRaisesRegex(ValueError, 'changed frozen input: package.json'): + self.audit(target) + def test_unknown_usage_or_global_scope_claim_rejected(self): self.qmd['whole_task_provider_usage'] = 0 with self.assertRaises(ValueError): @@ -207,17 +241,17 @@ def test_unknown_usage_or_global_scope_claim_rejected(self): self.audit() -class FutureCommandRecordingTests(unittest.TestCase): +class ArchivedCommandRecordingTests(unittest.TestCase): def record(self, directory, response=None, error=None): run = Path(directory) argv = [run/'owned tools/node', '--index', 'named index', 'aéé.md', '--input=' + str(run/'corpus/a file.md')] facts = [] result = {'facts': facts, 'cleanup': {'timed_out_commands': 0}} - with patch.object(RUNNER.subprocess, 'run', return_value=response, side_effect=error) as invoked: + with patch.object(RECORDING_ARCHIVE.subprocess, 'run', return_value=response, side_effect=error) as invoked: try: - RUNNER.record_command('test', argv, run, {'LANG': 'C.UTF-8'}, - {str(run): ''}, facts, result) + RECORDING_ARCHIVE.record_command('test', argv, run, {'LANG': 'C.UTF-8'}, + {str(run): ''}, facts, result) except (subprocess.TimeoutExpired, OSError): if error is None: raise @@ -256,5 +290,73 @@ def test_launch_error_retains_attempted_command(self): self.assertIsNone(result['facts'][0]['exit_code']) +class RetiredRunnerTests(unittest.TestCase): + def test_old_modes_fail_before_subprocess_or_output_creation(self): + for mode in ['source', 'qmd']: + with self.subTest(mode=mode), tempfile.TemporaryDirectory() as folder: + run = Path(folder)/'uncreated-output' + stderr, stdout = io.StringIO(), io.StringIO() + argv = [str(ROOT/'run.py'), '--mode', mode, '--run-dir', str(run)] + argv += ['--source', str(ROOT/'seed/planner.py'), '--rg', str(Path(folder)/'unused-rg'), + '--ast-grep', str(Path(folder)/'unused-ast-grep'), + '--node', str(Path(folder)/'unused-node'), '--package-prefix', folder] + with patch.object(sys, 'argv', argv), redirect_stderr(stderr), redirect_stdout(stdout), \ + patch.object(subprocess, 'run', side_effect=AssertionError('retired runner launched a subprocess')) as invoked: + try: + status = RUNNER.main() + except SystemExit as exc: + status = exc.code + self.assertEqual(status, 1) + self.assertFalse(run.exists(), 'retired mode created its output directory') + invoked.assert_not_called() + self.assertIn('retired', stderr.getvalue().lower()) + self.assertEqual(stdout.getvalue(), '') + + def test_offline_cli_rejects_changed_recording_aid_archive(self): + with tempfile.TemporaryDirectory() as folder: + target = Path(folder)/'leaf' + shutil.copytree(ROOT, target) + archive = target/'run-recording-aid.py.txt' + archive.write_bytes(archive.read_bytes()+b'\n') + result = subprocess.run([sys.executable, str(target/'audit.py')], + text=True, capture_output=True, check=False) + self.assertEqual(result.returncode, 1) + self.assertIn('changed retirement artifact: run-recording-aid.py.txt', result.stderr) + + def test_offline_cli_checks_retirement_without_qualifying_history(self): + result = subprocess.run([sys.executable, str(ROOT/'audit.py')], + text=True, capture_output=True, check=False) + self.assertEqual(result.returncode, 0, result.stderr) + assessment = json.loads(result.stdout) + self.assertFalse(assessment['native_acceptance_established']) + self.assertEqual(assessment['status'], 'incomplete_historical_reference') + self.assertIn('current_retirement_assessment', assessment) + current = assessment['current_retirement_assessment'] + self.assertTrue(current['artifacts_consistent']) + self.assertEqual(current['active_qmd_advisory_status'], 'unresolved') + self.assertFalse(current['native_npm_guard_acceptance_established']) + + def test_offline_cli_rejects_restored_install_routes(self): + for change in ['missing-runtime-guard', 'restored-dependency', 'restored-script']: + with self.subTest(change=change), tempfile.TemporaryDirectory() as folder: + target = Path(folder)/'leaf' + shutil.copytree(ROOT, target) + manifest = { + 'name': 'retired-wsl-retrieval', 'version': '1.0.0', 'private': True, + 'devEngines': {'runtime': {'name': 'retired-wsl-retrieval', 'onFail': 'error'}}, + } + if change == 'missing-runtime-guard': + del manifest['devEngines'] + elif change == 'restored-dependency': + manifest['dependencies'] = {'@tobilu/qmd': '2.8.3'} + else: + manifest['scripts'] = {'install': 'node replay.js'} + (target/'package.json').write_text(json.dumps(manifest)+'\n') + result = subprocess.run([sys.executable, str(target/'audit.py')], + text=True, capture_output=True, check=False) + self.assertEqual(result.returncode, 1) + self.assertIn('retirement manifest', result.stderr) + + if __name__ == '__main__': unittest.main()