diff --git a/.github/workflows/acquisition-readiness-scan.yml b/.github/workflows/acquisition-readiness-scan.yml index a825687a5..91cf0d186 100644 --- a/.github/workflows/acquisition-readiness-scan.yml +++ b/.github/workflows/acquisition-readiness-scan.yml @@ -61,7 +61,7 @@ jobs: - name: upload acquisition artifacts if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: acquisition-readiness-audit path: artifacts/acquisition-readiness/**/* diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 2df1ccbd8..1d3ba4584 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -260,7 +260,7 @@ jobs: - name: Upload durable deployment and rollback evidence if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: noema-deployment-evidence-production-${{ steps.release.outputs.tag }} path: | diff --git a/.github/workflows/hourly-commercial-readiness.yml b/.github/workflows/hourly-commercial-readiness.yml index 294debefc..91657db0c 100644 --- a/.github/workflows/hourly-commercial-readiness.yml +++ b/.github/workflows/hourly-commercial-readiness.yml @@ -107,7 +107,7 @@ jobs: - name: upload scheduler activation evidence if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: scheduler-activation-evidence path: artifacts/operations/hourly-scheduler-activation.json @@ -199,7 +199,7 @@ jobs: - name: upload main governance audit if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: main-governance-audit path: artifacts/governance/main-governance-audit.json @@ -208,7 +208,7 @@ jobs: - name: upload loop report if: always() && steps.loop.outcome != 'skipped' - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: commercial-readiness-loop-report path: artifacts/commercial-readiness/hourly-loop-report.json @@ -217,7 +217,7 @@ jobs: - name: upload no-PR commercial-readiness evidence if: always() && steps.loop.outputs.remaining_open_pull_request_count == '0' - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: no-pr-commercial-readiness-evidence path: | diff --git a/.github/workflows/readiness-scan.yml b/.github/workflows/readiness-scan.yml index 2b98770e6..72e0e395d 100644 --- a/.github/workflows/readiness-scan.yml +++ b/.github/workflows/readiness-scan.yml @@ -80,7 +80,7 @@ jobs: - name: upload readiness artifacts if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: saleable-readiness-audit path: | diff --git a/.github/workflows/release-evidence.yml b/.github/workflows/release-evidence.yml index 439d59851..6b546fa87 100644 --- a/.github/workflows/release-evidence.yml +++ b/.github/workflows/release-evidence.yml @@ -234,7 +234,7 @@ jobs: - name: Upload bounded sterile release materialization id: materialization_bundle - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: noema-release-materialization-${{ needs.verify_release.outputs.tag }} path: | @@ -404,7 +404,7 @@ jobs: - name: Upload bounded release publication bundle id: release_bundle - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: noema-release-evidence-${{ needs.materialize_release.outputs.tag }} path: | @@ -647,7 +647,7 @@ jobs: - name: Upload durable publication receipt if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: noema-release-publication-${{ needs.attest_release.outputs.tag }} path: ${{ runner.temp }}/release-publication/ diff --git a/test/acquisition-readiness-scan-toolchain-integrity.test.ts b/test/acquisition-readiness-scan-toolchain-integrity.test.ts index d5453c355..e1aaeb2d1 100644 --- a/test/acquisition-readiness-scan-toolchain-integrity.test.ts +++ b/test/acquisition-readiness-scan-toolchain-integrity.test.ts @@ -16,7 +16,10 @@ describe("acquisition-readiness workflow supply-chain integrity", () => { "uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0", ); expect(workflow).toContain( - "uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2", + "uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1", + ); + expect(workflow).not.toContain( + "actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02", ); expect(workflow).not.toMatch( /uses:\s+actions\/(?:checkout|setup-node|upload-artifact)@v\d+/, diff --git a/test/readiness-scan-toolchain-integrity.test.ts b/test/readiness-scan-toolchain-integrity.test.ts index a5942a5fb..49ea6476a 100644 --- a/test/readiness-scan-toolchain-integrity.test.ts +++ b/test/readiness-scan-toolchain-integrity.test.ts @@ -12,7 +12,10 @@ describe("saleable-readiness workflow supply-chain integrity", () => { "uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0", ); expect(workflow).toContain( - "uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2", + "uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1", + ); + expect(workflow).not.toContain( + "actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02", ); expect(workflow).not.toMatch(/uses:\s+actions\/(?:checkout|setup-node|upload-artifact)@v\d+/); }); diff --git a/test/upload-artifact-node24-integrity.test.ts b/test/upload-artifact-node24-integrity.test.ts new file mode 100644 index 000000000..db50c1454 --- /dev/null +++ b/test/upload-artifact-node24-integrity.test.ts @@ -0,0 +1,28 @@ +import { readFileSync } from "node:fs"; +import { describe, expect, it } from "vitest"; + +const uploadArtifactPin = + "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1"; +const deprecatedUploadArtifactPin = + "actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02"; +const supportedWorkflowPaths = [ + ".github/workflows/acquisition-readiness-scan.yml", + ".github/workflows/cd.yml", + ".github/workflows/central-review.yml", + ".github/workflows/hourly-commercial-readiness.yml", + ".github/workflows/hourly-product-development.yml", + ".github/workflows/maintainer-app-readiness.yml", + ".github/workflows/readiness-scan.yml", + ".github/workflows/release-evidence.yml", +] as const; + +describe("upload-artifact Node 24 supply-chain contract", () => { + for (const workflowPath of supportedWorkflowPaths) { + it(`${workflowPath} pins the reviewed Node 24 upload action`, () => { + const workflow = readFileSync(workflowPath, "utf8"); + expect(workflow).toContain(uploadArtifactPin); + expect(workflow).not.toContain(deprecatedUploadArtifactPin); + expect(workflow).not.toMatch(/uses:\s+actions\/upload-artifact@v\d+/); + }); + } +});