Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ version: 2
# (post-merge SBOM check in section 8). No pip version-update entry is added: a zizmor
# security-update PR fails the pin test and the evidence hash check by design, and the
# maintainer does the reviewed relock and closes it, as with #97/#98 -> #99.
# npm/pnpm and uv: the only manifests (blueprints/convergence-practice/application-delivery,
# blueprints/convergence-practice/wsl-retrieval, evidence/artifacts/macos-application-20260924/variant)
# npm/pnpm and uv: the only manifests that declare dependencies
# (blueprints/convergence-practice/application-delivery, evidence/artifacts/macos-application-20260924/variant)
# are frozen experiment or evidence artifacts whose lock hashes are registered in
# manifests/evidence.json and cited by receipts, so a version bump would break validate;
# application-delivery also pins pnpm 12, outside the pnpm versions Dependabot lists.
# blueprints/convergence-practice/wsl-retrieval keeps only a dependency-free package.json; its
# retired lock is package-lock.json.frozen (2026-10-04), a name the dependency graph does not read.
# NuGet: the 23 packages.lock.json files are overlays for an upstream LEAN checkout with
# no project file; neither Dependabot nor the dependency graph discovers them, and
# security-scan.yml's osv-scanner job is their only vulnerability-alert path.
Expand Down
12 changes: 0 additions & 12 deletions .github/osv-scanner-frozen-wsl-retrieval.toml

This file was deleted.

17 changes: 8 additions & 9 deletions .github/osv-scanner-lockfiles.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"description": "Every tracked dependency lockfile or manifest that security-scan.yml's osv-scanner job scans. A lockfile entry without a parser lets OSV-Scanner infer it from the file name; parser names the OSV-Scanner v2 extractor for names it cannot infer (--lockfile=<parser>:<path>). A covered_by_lockfile entry is a manifest OSV-Scanner v2.6.0 has no source extractor for; its sibling lockfile is scanned instead. An excluded entry is a tracked lockfile deliberately left out of the scan; it needs a reason and an evidence path, and only a deliberately vulnerable test fixture qualifies. A lockfile entry that names a config is scanned in an invocation of its own under that config alone (an explicit --config applies to every input of one invocation), and every other entry together under .github/osv-scanner.toml; the tests bind each such config to its one lock. tests/test_osv_lockfile_coverage.py fails when a tracked lockfile or manifest is in none of the three lists.",
"description": "Every tracked dependency lockfile or manifest that security-scan.yml's osv-scanner job scans. A lockfile entry without a parser lets OSV-Scanner infer it from the file name; parser names the OSV-Scanner v2 extractor for names it cannot infer (--lockfile=<parser>:<path>). A covered_by_lockfile entry is a manifest OSV-Scanner v2.6.0 has no source extractor for; its sibling lockfile is scanned instead. A dependency_free entry is a package.json that declares no dependency and has no lockfile beside it, so nothing is scanned for it; it needs a reason and an evidence path. An excluded entry is a tracked lockfile deliberately left out of the scan; it needs a reason and an evidence path, and only a deliberately vulnerable test fixture qualifies. A lockfile entry that names a config is scanned in an invocation of its own under that config alone (an explicit --config applies to every input of one invocation), and every other entry together under .github/osv-scanner.toml; the tests bind each such config to its one lock. tests/test_osv_lockfile_coverage.py fails when a tracked lockfile or manifest is in none of the four lists.",
"lockfiles": [
{
"path": "examples/claude-runtime-sdk/worker.py.lock",
Expand Down Expand Up @@ -47,10 +47,6 @@
{
"path": "blueprints/convergence-practice/application-delivery/uv.lock"
},
{
"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"
},
Expand Down Expand Up @@ -224,10 +220,6 @@
"path": "blueprints/convergence-practice/application-delivery/pyproject.toml",
"lockfile": "blueprints/convergence-practice/application-delivery/uv.lock"
},
{
"path": "blueprints/convergence-practice/wsl-retrieval/package.json",
"lockfile": "blueprints/convergence-practice/wsl-retrieval/package-lock.json"
},
{
"path": "blueprints/memory-stack/native-qualification/pyproject.toml",
"lockfile": "blueprints/memory-stack/native-qualification/uv.lock"
Expand Down Expand Up @@ -257,6 +249,13 @@
"lockfile": "evidence/artifacts/macos-syn-e2e-20260924/requirements-macos-arm64-py312.lock"
}
],
"dependency_free": [
{
"path": "blueprints/convergence-practice/wsl-retrieval/package.json",
"reason": "Retirement guard of the retired WSL retrieval partition: private, with no dependency, workspace, override or script field, and devEngines.runtime.name=retired-wsl-retrieval with onFail=error. Its historical lock is kept byte-identical as package-lock.json.frozen since 2026-10-04, a name no scanner reads, so there is no dependency here for OSV-Scanner or GitHub's dependency graph to report.",
"evidence": "evidence/receipts/wsl-lock-frozen-rename-20261004.json"
}
],
"excluded": [
{
"path": "evidence/artifacts/gap-wave2-20260923/us-equities__identity-provenance/raw/dvc-fixround3/repo-git__dvc.lock",
Expand Down
47 changes: 12 additions & 35 deletions .github/workflows/security-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ 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 the dedicated frozen macOS and retired WSL configs named by those entries.
# .github/osv-scanner.toml, or the dedicated frozen macOS config named by its entry.
# 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;
# each dedicated config to its exact reviewed lock. Two 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.
Expand Down Expand Up @@ -66,11 +66,10 @@ 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
# all three SARIF results are still written, then fail with the worst observed status.
# both 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 \
Expand All @@ -89,54 +88,40 @@ jobs:
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
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
# Every entry is in exactly one scan: together the two 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" '
test "$(( ${#lockfiles[@]} + ${#frozen[@]} ))" -eq "$(jq '.lockfiles | length' "$inventory")" || exit 1
# Also enforce unique inputs and the exact archive assignment in the shell caller itself.
jq -e --arg mac_config "$frozen_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"])
["evidence/artifacts/macos-application-20260924/variant/pnpm-lock.yaml"])
' "$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"
statuses=("$status" "$frozen_status")
printf 'OSV primary exit codes: ordinary=%s frozen-macos=%s\n' "$status" "$frozen_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"
statuses+=("$sarif_status" "$frozen_sarif_status")
printf 'OSV SARIF exit codes: ordinary=%s frozen-macos=%s\n' "$sarif_status" "$frozen_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.
Expand All @@ -156,7 +141,6 @@ 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

Expand Down Expand Up @@ -195,13 +179,6 @@ 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
# Keep this report uploadable after an earlier upload fails.
if: ${{ !cancelled() }}
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'
Expand Down
43 changes: 39 additions & 4 deletions blueprints/convergence-practice/wsl-retrieval/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
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.
manifest and recording aid are preserved as text artifacts. Since 2026-10-04 the
retained lock is stored byte-identical as [package-lock.json.frozen](package-lock.json.frozen),
a name no dependency scanner reads ([below](#lock-renamed-out-of-scanner-discovery-2026-10-04)).
Neither the retirement nor the rename patches the dependency, and no scanner
exception remains for it.

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).
Expand Down Expand Up @@ -35,7 +37,7 @@ The source fixture contains the frozen planner from repository revision
`6f74bc503ccecaaf6ccebd53677b23aedab50921`. Its selected ripgrep **15.2.0** and
ast-grep **0.45.3** results contain exact file lines, byte spans and source text.
The QMD **2.8.3** fixture reports Node **24.21.0**, better-sqlite3 **13.0.3** and
sqlite-vec **0.1.9**, with the retained [package lock](package-lock.json). It covers
sqlite-vec **0.1.9**, with the retained [package lock](package-lock.json.frozen). It covers
five positive and two negative queries, Unicode, a one-line `get`, process reopen,
update and deletion in a three-document primary collection plus one decoy.
The final recorded database has integrity `ok`, three active documents and zero
Expand Down Expand Up @@ -107,6 +109,12 @@ 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.
It also requires the lock to exist only as `package-lock.json.frozen` at its digest,
tracked in Git, and no `package-lock.json`, `npm-shrinkwrap.json`, `yarn.lock`,
`pnpm-lock.yaml`, `bun.lock` or `deno.lock` anywhere in this directory, in any letter
case, on disk or in the Git index; it fails closed when Git cannot list the files.
Scenario regressions on scratch Git repositories and code mutants of the guard
(`LockDiscoveryGuardTests`) show each of those requirements is enforced.
These are local integration and artifact checks; they do not establish native npm
guard acceptance or a scanner exception.

Expand All @@ -130,3 +138,30 @@ 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.

## Lock renamed out of scanner discovery (2026-10-04)

The retained lock moved from `package-lock.json` to `package-lock.json.frozen` with
`git mv`. Its bytes are unchanged: 82,463 bytes, SHA-256
`5c51ee65cc477f2c1488a38ff5cad1c0a737f81a5b61bbd70d5edc4d15bfc3bb`.

GitHub's dependency graph, Scorecard's OSV-Scanner run and OSV-Scanner itself find
lockfiles by file name. Under its npm name this lock raised Dependabot alert 17
(braces 3.0.3, GHSA-vfj7-8cjw-p6xm, high, no patched release) and the Scorecard
code-scanning alert 19 that carries the same advisory. It also needed a dedicated
OSV-Scanner grant, due to expire on 2026-10-17. No scanner reads the `.frozen` name,
so that grant and its scan group are deleted. "Fixed" means the lock was removed
from discovery, not that braces was patched: the archived bytes still pin braces
3.0.3. Active QMD's own exposure to the advisory is unchanged and unresolved.

The receipts, the install inventory and the 2026-10-03 retirement assessment still
name the lock `package-lock.json`. The audit resolves that name to the archive, as it
resolves `package.json` to `package-original.json.txt`, and no receipt was rewritten.
The convergence record changes only the lock's path, with the same SHA-256, and the
evaluation digests of `audit.py` and `tests/test_wsl_retrieval.py`. This directory
still offers no supported way to restore or install the lock.

Decision: the 2026-10-04 addendum to
[2026-09-25-longmemeval-frozen-npm-lock.md](../../../docs/decisions/2026-09-25-longmemeval-frozen-npm-lock.md).
OSV-Scanner controls and the guard's mutants:
[wsl-lock-frozen-rename-20261004.json](../../../evidence/receipts/wsl-lock-frozen-rename-20261004.json).
Loading
Loading