From d34c0b9d9c49d7ece8a5301e7e539c1f11139584 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 11 Aug 2026 14:17:31 +0900 Subject: [PATCH 1/8] test(acquisition): require immutable audit toolchain --- ...readiness-scan-toolchain-integrity.test.ts | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 test/acquisition-readiness-scan-toolchain-integrity.test.ts diff --git a/test/acquisition-readiness-scan-toolchain-integrity.test.ts b/test/acquisition-readiness-scan-toolchain-integrity.test.ts new file mode 100644 index 000000000..c97ced321 --- /dev/null +++ b/test/acquisition-readiness-scan-toolchain-integrity.test.ts @@ -0,0 +1,36 @@ +import { readFileSync } from "node:fs"; +import { describe, expect, it } from "vitest"; + +const workflow = readFileSync( + ".github/workflows/acquisition-readiness-scan.yml", + "utf8", +); + +describe("acquisition-readiness workflow supply-chain integrity", () => { + it("pins trusted actions and refuses persisted checkout credentials", () => { + expect(workflow).toContain( + "uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2", + ); + expect(workflow).toContain("persist-credentials: false"); + expect(workflow).toContain( + "uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0", + ); + expect(workflow).toContain( + "uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2", + ); + expect(workflow).not.toMatch( + /uses:\s+actions\/(?:checkout|setup-node|upload-artifact)@v\d+/, + ); + }); + + it("uses exact Node/npm identities and frozen install semantics", () => { + expect(workflow).toContain('node-version: "24.19.0"'); + expect(workflow).toContain('test "$(node --version)" = "v24.19.0"'); + expect(workflow).toContain('test "$(npm --version)" = "11.17.0"'); + expect(workflow).toContain( + "npm ci --legacy-peer-deps=false --install-links=false", + ); + expect(workflow).not.toContain('node-version: "24"'); + expect(workflow).not.toMatch(/run:\s+npm ci\s*(?:\n|$)/); + }); +}); From 168766a7a67314f403019b089ea379f639ee5a7d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 11 Aug 2026 14:18:07 +0900 Subject: [PATCH 2/8] fix(acquisition): pin scheduled audit toolchain --- .../workflows/acquisition-readiness-scan.yml | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/.github/workflows/acquisition-readiness-scan.yml b/.github/workflows/acquisition-readiness-scan.yml index 840f33f1c..a0cdd8f0c 100644 --- a/.github/workflows/acquisition-readiness-scan.yml +++ b/.github/workflows/acquisition-readiness-scan.yml @@ -15,16 +15,25 @@ jobs: steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: setup node - uses: actions/setup-node@v4 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: - node-version: "24" + node-version: "24.19.0" cache: npm + - name: verify package-manager toolchain + shell: bash + run: | + set -euo pipefail + test "$(node --version)" = "v24.19.0" + test "$(npm --version)" = "11.17.0" + - name: install - run: npm ci + run: npm ci --legacy-peer-deps=false --install-links=false - name: build data-room manifest run: npm run acquisition:manifest @@ -44,7 +53,7 @@ jobs: - name: upload acquisition artifacts if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: acquisition-readiness-audit path: artifacts/acquisition-readiness/**/* From e1a78b8ac5b4c9f16614899e2763ad622ce39ffa Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 11 Aug 2026 15:19:00 +0900 Subject: [PATCH 3/8] test(acquisition): reject post-install tracked source drift --- ...n-readiness-scan-toolchain-integrity.test.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/acquisition-readiness-scan-toolchain-integrity.test.ts b/test/acquisition-readiness-scan-toolchain-integrity.test.ts index c97ced321..658d48c3d 100644 --- a/test/acquisition-readiness-scan-toolchain-integrity.test.ts +++ b/test/acquisition-readiness-scan-toolchain-integrity.test.ts @@ -33,4 +33,21 @@ describe("acquisition-readiness workflow supply-chain integrity", () => { expect(workflow).not.toContain('node-version: "24"'); expect(workflow).not.toMatch(/run:\s+npm ci\s*(?:\n|$)/); }); + + it("revalidates tracked buyer-evidence source after npm lifecycle execution", () => { + const installIndex = workflow.indexOf(" - name: install"); + const integrityIndex = workflow.indexOf( + " - name: verify tracked acquisition source unchanged after install", + ); + const manifestIndex = workflow.indexOf(" - name: build data-room manifest"); + + expect(installIndex).toBeGreaterThan(-1); + expect(integrityIndex).toBeGreaterThan(installIndex); + expect(manifestIndex).toBeGreaterThan(integrityIndex); + + const integrityBlock = workflow.slice(integrityIndex, manifestIndex); + expect(integrityBlock).toContain("git status --porcelain=v1 --untracked-files=no"); + expect(integrityBlock).toContain('git rev-parse HEAD'); + expect(integrityBlock).toContain('github.sha'); + }); }); From 28f2157c4d34138588816793e3b9e8c6f94b6a4c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 11 Aug 2026 15:23:40 +0900 Subject: [PATCH 4/8] fix(acquisition): reject post-install tracked source drift --- .github/workflows/acquisition-readiness-scan.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/acquisition-readiness-scan.yml b/.github/workflows/acquisition-readiness-scan.yml index a0cdd8f0c..9ccda9988 100644 --- a/.github/workflows/acquisition-readiness-scan.yml +++ b/.github/workflows/acquisition-readiness-scan.yml @@ -35,6 +35,17 @@ jobs: - name: install run: npm ci --legacy-peer-deps=false --install-links=false + - name: verify tracked acquisition source unchanged after install + shell: bash + run: | + set -euo pipefail + tracked_drift="$(git status --porcelain=v1 --untracked-files=no)" + if [ -n "$tracked_drift" ]; then + printf '::error::Dependency installation changed tracked acquisition source:\n%s\n' "$tracked_drift" + exit 1 + fi + test "$(git rev-parse HEAD)" = "${{ github.sha }}" + - name: build data-room manifest run: npm run acquisition:manifest From 07a6514250c8100f0362c28391a855c6033a7809 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 11 Aug 2026 16:04:05 +0900 Subject: [PATCH 5/8] test(acquisition): forbid dependency install in evidence audit --- ...on-readiness-scan-toolchain-integrity.test.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/test/acquisition-readiness-scan-toolchain-integrity.test.ts b/test/acquisition-readiness-scan-toolchain-integrity.test.ts index 658d48c3d..7d3a9b112 100644 --- a/test/acquisition-readiness-scan-toolchain-integrity.test.ts +++ b/test/acquisition-readiness-scan-toolchain-integrity.test.ts @@ -23,26 +23,22 @@ describe("acquisition-readiness workflow supply-chain integrity", () => { ); }); - it("uses exact Node/npm identities and frozen install semantics", () => { + it("uses exact Node/npm identities without installing dependency code", () => { expect(workflow).toContain('node-version: "24.19.0"'); expect(workflow).toContain('test "$(node --version)" = "v24.19.0"'); expect(workflow).toContain('test "$(npm --version)" = "11.17.0"'); - expect(workflow).toContain( - "npm ci --legacy-peer-deps=false --install-links=false", - ); expect(workflow).not.toContain('node-version: "24"'); - expect(workflow).not.toMatch(/run:\s+npm ci\s*(?:\n|$)/); + expect(workflow).not.toMatch(/\bnpm\s+(?:ci|install|i)\b/); + expect(workflow).not.toContain(" - name: install"); }); - it("revalidates tracked buyer-evidence source after npm lifecycle execution", () => { - const installIndex = workflow.indexOf(" - name: install"); + it("revalidates tracked buyer-evidence source before evidence generation", () => { const integrityIndex = workflow.indexOf( - " - name: verify tracked acquisition source unchanged after install", + " - name: verify tracked acquisition source before evidence generation", ); const manifestIndex = workflow.indexOf(" - name: build data-room manifest"); - expect(installIndex).toBeGreaterThan(-1); - expect(integrityIndex).toBeGreaterThan(installIndex); + expect(integrityIndex).toBeGreaterThan(-1); expect(manifestIndex).toBeGreaterThan(integrityIndex); const integrityBlock = workflow.slice(integrityIndex, manifestIndex); From 358eaa053ee7de7bcaa0a412ea858cb61cd376a9 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 11 Aug 2026 16:04:41 +0900 Subject: [PATCH 6/8] fix(acquisition): avoid dependency execution in evidence audit --- .github/workflows/acquisition-readiness-scan.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/acquisition-readiness-scan.yml b/.github/workflows/acquisition-readiness-scan.yml index 9ccda9988..9693b06a4 100644 --- a/.github/workflows/acquisition-readiness-scan.yml +++ b/.github/workflows/acquisition-readiness-scan.yml @@ -32,16 +32,13 @@ jobs: test "$(node --version)" = "v24.19.0" test "$(npm --version)" = "11.17.0" - - name: install - run: npm ci --legacy-peer-deps=false --install-links=false - - - name: verify tracked acquisition source unchanged after install + - name: verify tracked acquisition source before evidence generation shell: bash run: | set -euo pipefail tracked_drift="$(git status --porcelain=v1 --untracked-files=no)" if [ -n "$tracked_drift" ]; then - printf '::error::Dependency installation changed tracked acquisition source:\n%s\n' "$tracked_drift" + printf '::error::Tracked acquisition source changed before evidence generation:\n%s\n' "$tracked_drift" exit 1 fi test "$(git rev-parse HEAD)" = "${{ github.sha }}" From 9d0043a68467e3efb112988ca3e9e5be09152c24 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 11 Aug 2026 17:12:41 +0900 Subject: [PATCH 7/8] test(acquisition): require retained audit artifact --- ...acquisition-readiness-scan-toolchain-integrity.test.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/acquisition-readiness-scan-toolchain-integrity.test.ts b/test/acquisition-readiness-scan-toolchain-integrity.test.ts index 7d3a9b112..d5453c355 100644 --- a/test/acquisition-readiness-scan-toolchain-integrity.test.ts +++ b/test/acquisition-readiness-scan-toolchain-integrity.test.ts @@ -46,4 +46,12 @@ describe("acquisition-readiness workflow supply-chain integrity", () => { expect(integrityBlock).toContain('git rev-parse HEAD'); expect(integrityBlock).toContain('github.sha'); }); + + it("fails closed when retained acquisition evidence is missing", () => { + const uploadIndex = workflow.indexOf(" - name: upload acquisition artifacts"); + expect(uploadIndex).toBeGreaterThan(-1); + const uploadBlock = workflow.slice(uploadIndex); + expect(uploadBlock).toContain("if-no-files-found: error"); + expect(uploadBlock).not.toContain("if-no-files-found: warn"); + }); }); From a9f22dab16e4c81456350e41d477fd623665919c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 11 Aug 2026 17:14:22 +0900 Subject: [PATCH 8/8] fix(acquisition): fail closed on missing audit artifact --- .github/workflows/acquisition-readiness-scan.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/acquisition-readiness-scan.yml b/.github/workflows/acquisition-readiness-scan.yml index 9693b06a4..a825687a5 100644 --- a/.github/workflows/acquisition-readiness-scan.yml +++ b/.github/workflows/acquisition-readiness-scan.yml @@ -65,4 +65,4 @@ jobs: with: name: acquisition-readiness-audit path: artifacts/acquisition-readiness/**/* - if-no-files-found: warn + if-no-files-found: error