diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 434deb35..c169bbd8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,15 +13,15 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Setup Node.js 22 - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: "22" - name: Setup pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 # Do not pin `version:` here — it conflicts with package.json packageManager # (pnpm/action-setup v4 fails the job before tests or publish can run). @@ -58,10 +58,10 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Setup Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 with: python-version: ${{ matrix.python-version }} @@ -77,15 +77,15 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Setup Node.js 22 - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: "22" - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 with: python-version: "3.12" @@ -105,15 +105,15 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Setup Node.js 22 - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: "22" - name: Setup pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 - name: Install dependencies run: pnpm install --frozen-lockfile @@ -125,7 +125,7 @@ jobs: npm pack --ignore-scripts --pack-destination "$RUNNER_TEMP/candidates" - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 with: python-version: "3.12" @@ -136,7 +136,7 @@ jobs: python3 -m build --outdir "$RUNNER_TEMP/candidates-py" packages/vantio-agent-sdk-py - name: Upload candidates - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: release-candidates path: | @@ -149,10 +149,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 with: python-version: "3.12" diff --git a/.github/workflows/docs-release-governance.yml b/.github/workflows/docs-release-governance.yml index 49fe075b..c2251df6 100644 --- a/.github/workflows/docs-release-governance.yml +++ b/.github/workflows/docs-release-governance.yml @@ -13,12 +13,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: persist-credentials: false - name: Setup Node.js 22 - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: "22" diff --git a/.github/workflows/enterprise-slsa-provenance.yml b/.github/workflows/enterprise-slsa-provenance.yml index c3fa931e..e06460c6 100755 --- a/.github/workflows/enterprise-slsa-provenance.yml +++ b/.github/workflows/enterprise-slsa-provenance.yml @@ -29,15 +29,15 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Setup Node.js 22 - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: "22" - name: Setup pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 # Do not pin `version:` here — it conflicts with package.json packageManager. - name: Install dependencies @@ -59,7 +59,7 @@ jobs: - name: Upload release bundle (tags + manual) if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: vantio-artifacts-${{ github.sha }} path: | @@ -69,7 +69,7 @@ jobs: - name: Attest build provenance id: attest - uses: actions/attest-build-provenance@v2 + uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2 with: subject-path: vantio-artifacts.tar.gz diff --git a/.github/workflows/mcp-registry-publish.yml b/.github/workflows/mcp-registry-publish.yml index b346c20e..8ba748ee 100644 --- a/.github/workflows/mcp-registry-publish.yml +++ b/.github/workflows/mcp-registry-publish.yml @@ -26,7 +26,7 @@ jobs: environment: mcp-registry-publish steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: persist-credentials: false diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index a9e64997..58c50615 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -40,13 +40,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout source commit - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: ref: ${{ inputs.source_commit }} persist-credentials: false - name: Setup Node.js 22 - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: "22" @@ -78,13 +78,13 @@ jobs: actions: write steps: - name: Checkout source commit - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: ref: ${{ inputs.source_commit }} persist-credentials: false - name: Setup Node.js 22 - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: "22" @@ -130,7 +130,7 @@ jobs: PY - name: Upload approval record - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: release-approval path: release-approval.json diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index 3efc1697..cbff924b 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -68,7 +68,7 @@ jobs: id-token: write steps: - name: Checkout release scripts - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: persist-credentials: false sparse-checkout: | @@ -127,7 +127,7 @@ jobs: --upload-dir "$GITHUB_WORKSPACE/pypi-sealed-upload" - name: Publish sealed distributions to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1 with: packages-dir: pypi-sealed-upload verbose: false diff --git a/.github/workflows/vantio-prove-example.yml b/.github/workflows/vantio-prove-example.yml index f8af4e4e..f85c042e 100644 --- a/.github/workflows/vantio-prove-example.yml +++ b/.github/workflows/vantio-prove-example.yml @@ -9,7 +9,7 @@ jobs: prove: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - name: Generate proof uses: ./.github/actions/vantio-prove with: diff --git a/.github/workflows/w3-lab-teardown-verify.yml b/.github/workflows/w3-lab-teardown-verify.yml index aa38e1d1..ba78c9b2 100644 --- a/.github/workflows/w3-lab-teardown-verify.yml +++ b/.github/workflows/w3-lab-teardown-verify.yml @@ -89,7 +89,7 @@ jobs: test "$GITHUB_WORKFLOW_REF" = "vantioai/vantio-open-core/.github/workflows/w3-lab-teardown-verify.yml@refs/heads/main" - name: Checkout preflight - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: persist-credentials: false sparse-checkout: | @@ -134,7 +134,7 @@ jobs: fi - name: Checkout verifier - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: persist-credentials: false sparse-checkout: | @@ -144,7 +144,7 @@ jobs: - name: Assume vantio-w3-lab-teardown id: aws continue-on-error: true - uses: aws-actions/configure-aws-credentials@v6 + uses: aws-actions/configure-aws-credentials@e1253824e5c10ff9df46874f81ed3ec929e19cfd # v6 with: role-to-assume: arn:aws:iam::960577828987:role/vantio-w3-lab-teardown role-session-name: w3-lab-teardown-verify-${{ github.run_id }} @@ -176,7 +176,7 @@ jobs: - name: Upload verification artifact if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: w3-lab-teardown-verify-${{ github.run_id }} path: w3-lab-teardown-verify.json diff --git a/docs/programs/release-engineering/00-INVENTORY.md b/docs/programs/release-engineering/00-INVENTORY.md index 3fb91bf0..c0c9bbc5 100644 --- a/docs/programs/release-engineering/00-INVENTORY.md +++ b/docs/programs/release-engineering/00-INVENTORY.md @@ -39,15 +39,15 @@ Python wheel `vantio_agent_sdk-3.1.0-py3-none-any.whl` is pinned at 39235 bytes, - Root `packageManager` names pnpm `11.13.0` and carries a `sha512` integrity string. - `pnpm-lock.yaml` is present. Its SHA-256 is in the pin report. Lockfile importers record resolved versions for the workspace dependency ranges. - `scripts/release/stage_sealed_pypi.py` pins the Python 3.1.0 filenames, byte lengths, and SHA-256 values. Dispatch inputs cannot replace them. -- GitHub Action references under `.github/` use floating refs such as `@v4` and `@release/v1`. The pin report records `digest_pinned: false` for each. +- Workflow files under `.github/workflows/` pin third-party actions to commit SHAs with tag comments. The pin report records those as `owner/name@sha # tag` with `digest_pinned: true`. `.github/actions/vantio-prove/action.yml` still uses floating `@v4` refs, and those two stay `digest_pinned: false`. - `packages/vantio-agent-sdk-py/pyproject.toml` requires `hatchling` with no version comparator. -- CI and the provenance workflow install with `pnpm install --frozen-lockfile`. That binds the install to the lockfile. It leaves the Action refs and the Python build backend unpinned. +- CI and the provenance workflow install with `pnpm install --frozen-lockfile`. That binds the install to the lockfile. The local composite action refs and the Python build backend stay unpinned. `npm-publish.yml`, `pypi-publish.yml`, and `mcp-registry-publish.yml` trigger on `workflow_dispatch`. `ci.yml` triggers on push and builds candidate artifacts. The candidate job packs and builds. It performs no registry write. ## Provenance characterization -`.github/workflows/enterprise-slsa-provenance.yml` can request `actions/attest-build-provenance@v2` for a tarball of `packages/vantio-agent-sdk/dist` and `packages/vantio-cli/bin`. The workflow comment withholds an in-repo verification. This force did not copy an attestation bundle into the tree. +`.github/workflows/enterprise-slsa-provenance.yml` requests `actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2` for a tarball of `packages/vantio-agent-sdk/dist` and `packages/vantio-cli/bin`. The workflow comment withholds an in-repo verification. This force did not copy an attestation bundle into the tree. `architecture_state.md` Phase VIII is a historical log. The pin report sets `historical_log_contains_level_assertion` from that log. The same log names `apps/web` and `packages/edge-proxy`. Both paths are absent. The workflow file on this commit attests a smaller bundle. WS11 claim tokens stay `NOT_CLAIMED`. diff --git a/docs/programs/release-engineering/dossiers/optics-public.json b/docs/programs/release-engineering/dossiers/optics-public.json index 45606d1d..35d20801 100644 --- a/docs/programs/release-engineering/dossiers/optics-public.json +++ b/docs/programs/release-engineering/dossiers/optics-public.json @@ -70,244 +70,6 @@ "name": ".github/actions/vantio-prove/action.yml actions/upload-artifact@v4", "required_for_publish": true, "value": "actions/upload-artifact@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/ci.yml actions/checkout@v4", - "required_for_publish": true, - "value": "actions/checkout@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/ci.yml actions/setup-node@v4", - "required_for_publish": true, - "value": "actions/setup-node@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/ci.yml pnpm/action-setup@v4", - "required_for_publish": true, - "value": "pnpm/action-setup@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/ci.yml actions/checkout@v4", - "required_for_publish": true, - "value": "actions/checkout@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/ci.yml actions/setup-python@v5", - "required_for_publish": true, - "value": "actions/setup-python@v5" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/ci.yml actions/checkout@v4", - "required_for_publish": true, - "value": "actions/checkout@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/ci.yml actions/setup-node@v4", - "required_for_publish": true, - "value": "actions/setup-node@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/ci.yml actions/setup-python@v5", - "required_for_publish": true, - "value": "actions/setup-python@v5" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/ci.yml actions/checkout@v4", - "required_for_publish": true, - "value": "actions/checkout@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/ci.yml actions/setup-node@v4", - "required_for_publish": true, - "value": "actions/setup-node@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/ci.yml pnpm/action-setup@v4", - "required_for_publish": true, - "value": "pnpm/action-setup@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/ci.yml actions/setup-python@v5", - "required_for_publish": true, - "value": "actions/setup-python@v5" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/ci.yml actions/upload-artifact@v4", - "required_for_publish": true, - "value": "actions/upload-artifact@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/ci.yml actions/checkout@v4", - "required_for_publish": true, - "value": "actions/checkout@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/ci.yml actions/setup-python@v5", - "required_for_publish": true, - "value": "actions/setup-python@v5" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/docs-release-governance.yml actions/checkout@v4", - "required_for_publish": true, - "value": "actions/checkout@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/docs-release-governance.yml actions/setup-node@v4", - "required_for_publish": true, - "value": "actions/setup-node@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/enterprise-slsa-provenance.yml actions/checkout@v4", - "required_for_publish": true, - "value": "actions/checkout@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/enterprise-slsa-provenance.yml actions/setup-node@v4", - "required_for_publish": true, - "value": "actions/setup-node@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/enterprise-slsa-provenance.yml pnpm/action-setup@v4", - "required_for_publish": true, - "value": "pnpm/action-setup@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/enterprise-slsa-provenance.yml actions/upload-artifact@v4", - "required_for_publish": true, - "value": "actions/upload-artifact@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/enterprise-slsa-provenance.yml actions/attest-build-provenance@v2", - "required_for_publish": true, - "value": "actions/attest-build-provenance@v2" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/mcp-registry-publish.yml actions/checkout@v4", - "required_for_publish": true, - "value": "actions/checkout@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/npm-publish.yml actions/checkout@v4", - "required_for_publish": true, - "value": "actions/checkout@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/npm-publish.yml actions/setup-node@v4", - "required_for_publish": true, - "value": "actions/setup-node@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/npm-publish.yml actions/checkout@v4", - "required_for_publish": true, - "value": "actions/checkout@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/npm-publish.yml actions/setup-node@v4", - "required_for_publish": true, - "value": "actions/setup-node@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/npm-publish.yml actions/upload-artifact@v4", - "required_for_publish": true, - "value": "actions/upload-artifact@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/pypi-publish.yml actions/checkout@v4", - "required_for_publish": true, - "value": "actions/checkout@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/pypi-publish.yml pypa/gh-action-pypi-publish@release/v1", - "required_for_publish": true, - "value": "pypa/gh-action-pypi-publish@release/v1" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/w3-lab-teardown-verify.yml actions/checkout@v4", - "required_for_publish": true, - "value": "actions/checkout@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/w3-lab-teardown-verify.yml actions/checkout@v4", - "required_for_publish": true, - "value": "actions/checkout@v4" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/w3-lab-teardown-verify.yml aws-actions/configure-aws-credentials@v6", - "required_for_publish": true, - "value": "aws-actions/configure-aws-credentials@v6" - }, - { - "accepted_as_pin": false, - "kind": "unpinned", - "name": ".github/workflows/w3-lab-teardown-verify.yml actions/upload-artifact@v4", - "required_for_publish": true, - "value": "actions/upload-artifact@v4" } ], "private_distribution": { @@ -328,7 +90,7 @@ "builds": [], "formal_claim": false, "reasons": [ - "GitHub Actions steps use floating refs such as @v4 and @release/v1.", + "Workflow third-party actions are commit SHA pins with tag comments. The local composite .github/actions/vantio-prove still uses floating @v4 refs.", "The Python build-system requirement is hatchling with no version comparator.", "No second build digest is recorded in this force." ], diff --git a/docs/programs/release-engineering/generated/evaluations.json b/docs/programs/release-engineering/generated/evaluations.json index 9eb1bc4c..3bf4a2ca 100644 --- a/docs/programs/release-engineering/generated/evaluations.json +++ b/docs/programs/release-engineering/generated/evaluations.json @@ -10,7 +10,7 @@ "release_success": false, "requirements": [ { - "detail": "pin python-build-hatchling is unpinned; pin node-toolchain is unpinned; pin cli-tarball is unrecorded; pin .github/actions/vantio-prove/action.yml actions/setup-node@v4 is unpinned; pin .github/actions/vantio-prove/action.yml actions/upload-artifact@v4 is unpinned; pin .github/workflows/ci.yml actions/checkout@v4 is unpinned; pin .github/workflows/ci.yml actions/setup-node@v4 is unpinned; pin .github/workflows/ci.yml pnpm/action-setup@v4 is unpinned; pin .github/workflows/ci.yml actions/checkout@v4 is unpinned; pin .github/workflows/ci.yml actions/setup-python@v5 is unpinned; pin .github/workflows/ci.yml actions/checkout@v4 is unpinned; pin .github/workflows/ci.yml actions/setup-node@v4 is unpinned; pin .github/workflows/ci.yml actions/setup-python@v5 is unpinned; pin .github/workflows/ci.yml actions/checkout@v4 is unpinned; pin .github/workflows/ci.yml actions/setup-node@v4 is unpinned; pin .github/workflows/ci.yml pnpm/action-setup@v4 is unpinned; pin .github/workflows/ci.yml actions/setup-python@v5 is unpinned; pin .github/workflows/ci.yml actions/upload-artifact@v4 is unpinned; pin .github/workflows/ci.yml actions/checkout@v4 is unpinned; pin .github/workflows/ci.yml actions/setup-python@v5 is unpinned; pin .github/workflows/docs-release-governance.yml actions/checkout@v4 is unpinned; pin .github/workflows/docs-release-governance.yml actions/setup-node@v4 is unpinned; pin .github/workflows/enterprise-slsa-provenance.yml actions/checkout@v4 is unpinned; pin .github/workflows/enterprise-slsa-provenance.yml actions/setup-node@v4 is unpinned; pin .github/workflows/enterprise-slsa-provenance.yml pnpm/action-setup@v4 is unpinned; pin .github/workflows/enterprise-slsa-provenance.yml actions/upload-artifact@v4 is unpinned; pin .github/workflows/enterprise-slsa-provenance.yml actions/attest-build-provenance@v2 is unpinned; pin .github/workflows/mcp-registry-publish.yml actions/checkout@v4 is unpinned; pin .github/workflows/npm-publish.yml actions/checkout@v4 is unpinned; pin .github/workflows/npm-publish.yml actions/setup-node@v4 is unpinned; pin .github/workflows/npm-publish.yml actions/checkout@v4 is unpinned; pin .github/workflows/npm-publish.yml actions/setup-node@v4 is unpinned; pin .github/workflows/npm-publish.yml actions/upload-artifact@v4 is unpinned; pin .github/workflows/pypi-publish.yml actions/checkout@v4 is unpinned; pin .github/workflows/pypi-publish.yml pypa/gh-action-pypi-publish@release/v1 is unpinned; pin .github/workflows/w3-lab-teardown-verify.yml actions/checkout@v4 is unpinned; pin .github/workflows/w3-lab-teardown-verify.yml actions/checkout@v4 is unpinned; pin .github/workflows/w3-lab-teardown-verify.yml aws-actions/configure-aws-credentials@v6 is unpinned; pin .github/workflows/w3-lab-teardown-verify.yml actions/upload-artifact@v4 is unpinned", + "detail": "pin python-build-hatchling is unpinned; pin node-toolchain is unpinned; pin cli-tarball is unrecorded; pin .github/actions/vantio-prove/action.yml actions/setup-node@v4 is unpinned; pin .github/actions/vantio-prove/action.yml actions/upload-artifact@v4 is unpinned", "id": "R1", "status": "GAP" }, diff --git a/docs/programs/release-engineering/generated/pin-report.json b/docs/programs/release-engineering/generated/pin-report.json index 7760364a..044b1d54 100644 --- a/docs/programs/release-engineering/generated/pin-report.json +++ b/docs/programs/release-engineering/generated/pin-report.json @@ -11,174 +11,179 @@ "uses": "actions/upload-artifact@v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/ci.yml", - "uses": "actions/checkout@v4" + "uses": "actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/ci.yml", - "uses": "actions/setup-node@v4" + "uses": "actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/ci.yml", - "uses": "pnpm/action-setup@v4" + "uses": "pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/ci.yml", - "uses": "actions/checkout@v4" + "uses": "actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/ci.yml", - "uses": "actions/setup-python@v5" + "uses": "actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/ci.yml", - "uses": "actions/checkout@v4" + "uses": "actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/ci.yml", - "uses": "actions/setup-node@v4" + "uses": "actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/ci.yml", - "uses": "actions/setup-python@v5" + "uses": "actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/ci.yml", - "uses": "actions/checkout@v4" + "uses": "actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/ci.yml", - "uses": "actions/setup-node@v4" + "uses": "actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/ci.yml", - "uses": "pnpm/action-setup@v4" + "uses": "pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/ci.yml", - "uses": "actions/setup-python@v5" + "uses": "actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/ci.yml", - "uses": "actions/upload-artifact@v4" + "uses": "actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/ci.yml", - "uses": "actions/checkout@v4" + "uses": "actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/ci.yml", - "uses": "actions/setup-python@v5" + "uses": "actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/docs-release-governance.yml", - "uses": "actions/checkout@v4" + "uses": "actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/docs-release-governance.yml", - "uses": "actions/setup-node@v4" + "uses": "actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/enterprise-slsa-provenance.yml", - "uses": "actions/checkout@v4" + "uses": "actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/enterprise-slsa-provenance.yml", - "uses": "actions/setup-node@v4" + "uses": "actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/enterprise-slsa-provenance.yml", - "uses": "pnpm/action-setup@v4" + "uses": "pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/enterprise-slsa-provenance.yml", - "uses": "actions/upload-artifact@v4" + "uses": "actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/enterprise-slsa-provenance.yml", - "uses": "actions/attest-build-provenance@v2" + "uses": "actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/mcp-registry-publish.yml", - "uses": "actions/checkout@v4" + "uses": "actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/npm-publish.yml", - "uses": "actions/checkout@v4" + "uses": "actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/npm-publish.yml", - "uses": "actions/setup-node@v4" + "uses": "actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/npm-publish.yml", - "uses": "actions/checkout@v4" + "uses": "actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/npm-publish.yml", - "uses": "actions/setup-node@v4" + "uses": "actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/npm-publish.yml", - "uses": "actions/upload-artifact@v4" + "uses": "actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/pypi-publish.yml", - "uses": "actions/checkout@v4" + "uses": "actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/pypi-publish.yml", - "uses": "pypa/gh-action-pypi-publish@release/v1" + "uses": "pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1" }, { - "digest_pinned": false, + "digest_pinned": true, + "file": ".github/workflows/vantio-prove-example.yml", + "uses": "actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4" + }, + { + "digest_pinned": true, "file": ".github/workflows/w3-lab-teardown-verify.yml", - "uses": "actions/checkout@v4" + "uses": "actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/w3-lab-teardown-verify.yml", - "uses": "actions/checkout@v4" + "uses": "actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/w3-lab-teardown-verify.yml", - "uses": "aws-actions/configure-aws-credentials@v6" + "uses": "aws-actions/configure-aws-credentials@e1253824e5c10ff9df46874f81ed3ec929e19cfd # v6" }, { - "digest_pinned": false, + "digest_pinned": true, "file": ".github/workflows/w3-lab-teardown-verify.yml", - "uses": "actions/upload-artifact@v4" + "uses": "actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4" } ], "audience": "INTERNAL_RESTRICTED", @@ -202,7 +207,7 @@ }, "provenance_workflow": { "apps_web_present": false, - "attest_action": "actions/attest-build-provenance@v2", + "attest_action": "actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2", "edge_proxy_present": false, "formal_slsa_level": "NOT_CLAIMED", "historical_log_contains_level_assertion": true, diff --git a/scripts/aws/test_w3_lab_teardown_workflow.py b/scripts/aws/test_w3_lab_teardown_workflow.py index acf1767a..e0cffe94 100644 --- a/scripts/aws/test_w3_lab_teardown_workflow.py +++ b/scripts/aws/test_w3_lab_teardown_workflow.py @@ -16,6 +16,9 @@ SCRIPT_PATH = ROOT / "scripts" / "aws" / "verify_w3_lab_teardown.py" ROLE_ARN = "arn:aws:iam::960577828987:role/vantio-w3-lab-teardown" POLICY_SHA256 = "2fd3909fe84cbe93b15c5525ece0d247d0f4f4a91e333346001d512e1efc5215" +CHECKOUT_USES = "actions/checkout@11d5960a326750d5838078e36cf38b85af677262" +UPLOAD_USES = "actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02" +AWS_USES = "aws-actions/configure-aws-credentials@e1253824e5c10ff9df46874f81ed3ec929e19cfd" def trigger_of(document: dict) -> dict: @@ -60,7 +63,8 @@ def test_oidc_permissions_environment_and_role(self) -> None: self.assertEqual(self.job["timeout-minutes"], 20) self.assertIs(self.doc["concurrency"]["cancel-in-progress"], False) settings = self.assume["with"] - self.assertEqual(self.assume["uses"], "aws-actions/configure-aws-credentials@v6") + self.assertEqual(self.assume["uses"], AWS_USES) + self.assertIn(f"uses: {AWS_USES} # v6", self.raw) self.assertEqual(settings["role-to-assume"], ROLE_ARN) self.assertEqual(settings["aws-region"], "us-east-2") self.assertEqual(str(settings["role-duration-seconds"]), "3600") @@ -85,11 +89,13 @@ def test_secret_outputs_are_not_passed_on(self) -> None: def test_checkout_and_artifact(self) -> None: checkout = self.steps[2] - self.assertEqual(checkout["uses"], "actions/checkout@v4") + self.assertEqual(checkout["uses"], CHECKOUT_USES) + self.assertIn(f"uses: {CHECKOUT_USES} # v4", self.raw) self.assertIs(checkout["with"]["persist-credentials"], False) self.assertIn("scripts/aws/verify_w3_lab_teardown.py", checkout["with"]["sparse-checkout"]) upload = self.steps[-1] - self.assertEqual(upload["uses"], "actions/upload-artifact@v4") + self.assertEqual(upload["uses"], UPLOAD_USES) + self.assertIn(f"uses: {UPLOAD_USES} # v4", self.raw) self.assertEqual(upload["if"], "always()") self.assertIn("w3-lab-teardown-verify.json", upload["with"]["path"]) joined = "\n".join(step.get("run", "") for step in self.steps) diff --git a/scripts/release/test_pypi_publish_workflow.py b/scripts/release/test_pypi_publish_workflow.py index 7bcc1c63..c0abac81 100644 --- a/scripts/release/test_pypi_publish_workflow.py +++ b/scripts/release/test_pypi_publish_workflow.py @@ -159,7 +159,9 @@ def test_step_order_and_checkout_cannot_supply_distributions(self) -> None: ], ) checkout = self.steps[0] - self.assertEqual(checkout["uses"], "actions/checkout@v4") + checkout_uses = "actions/checkout@11d5960a326750d5838078e36cf38b85af677262" + self.assertEqual(checkout["uses"], checkout_uses) + self.assertIn(f"uses: {checkout_uses} # v4", self.raw) self.assertIs(checkout["with"]["persist-credentials"], False) self.assertEqual( [line.strip() for line in checkout["with"]["sparse-checkout"].splitlines() if line.strip()], @@ -186,7 +188,9 @@ def test_step_order_and_checkout_cannot_supply_distributions(self) -> None: def test_publish_action_is_oidc_and_explicit(self) -> None: publish = self.steps[5] - self.assertEqual(publish["uses"], "pypa/gh-action-pypi-publish@release/v1") + publish_uses = "pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33" + self.assertEqual(publish["uses"], publish_uses) + self.assertIn(f"uses: {publish_uses} # release/v1", self.raw) self.assertEqual( set(publish["with"]), {"packages-dir", "verbose", "print-hash", "skip-existing"}, diff --git a/scripts/release/ws11/characterize.mjs b/scripts/release/ws11/characterize.mjs index d28a6a2a..348cd031 100644 --- a/scripts/release/ws11/characterize.mjs +++ b/scripts/release/ws11/characterize.mjs @@ -193,7 +193,7 @@ export function characterizeOptics(root, inventory = buildInventory(root)) { ...actionPins, ], reproducibility: assessment([ - "GitHub Actions steps use floating refs such as @v4 and @release/v1.", + "Workflow third-party actions are commit SHA pins with tag comments. The local composite .github/actions/vantio-prove still uses floating @v4 refs.", "The Python build-system requirement is hatchling with no version comparator.", "No second build digest is recorded in this force.", ]), diff --git a/scripts/release/ws11/inventory.mjs b/scripts/release/ws11/inventory.mjs index 3743d779..d7288015 100644 --- a/scripts/release/ws11/inventory.mjs +++ b/scripts/release/ws11/inventory.mjs @@ -6,7 +6,8 @@ import { fileURLToPath } from "node:url"; export const REPO_ROOT = join(dirname(fileURLToPath(import.meta.url)), "../../.."); const SKIP_DIRS = new Set([".git", "node_modules", "dist", "__pycache__", ".pytest_cache"]); -const BINDING_ACTION = /@(?:[0-9a-f]{40}|sha256:[0-9a-f]{64})$/i; +const BINDING_ACTION = /@(?:[0-9a-f]{40}|sha256:[0-9a-f]{64})(?:\s+#\s*\S+)?$/i; +const ACTION_USES_LINE = /^\s*(?:-\s+)?uses:\s*(\S+)(?:\s+#\s*(\S+))?\s*$/gm; export function sha256Text(text) { return createHash("sha256").update(text).digest("hex"); @@ -48,7 +49,7 @@ export function triggerHasPush(text) { } export function actionUses(text) { - return [...text.matchAll(/^\s*uses:\s*(\S+)\s*$/gm)].map((match) => match[1]); + return [...text.matchAll(ACTION_USES_LINE)].map((match) => (match[2] ? `${match[1]} # ${match[2]}` : match[1])); } function constString(source, name) { @@ -248,6 +249,12 @@ export function buildInventory(root) { if (py.wheel_bytes !== sealed.wheel_bytes || py.sdist_bytes !== sealed.sdist_bytes) { throw new Error("sealed release pin byte lengths differ from stage_sealed_pypi.py"); } + const attest = actions.find( + (action) => + action.file === ".github/workflows/enterprise-slsa-provenance.yml" && + action.uses.startsWith("actions/attest-build-provenance@"), + ); + if (!attest) throw new Error("enterprise-slsa-provenance.yml is missing actions/attest-build-provenance"); const pyproject = readText(root, "packages/vantio-agent-sdk-py/pyproject.toml"); const hatch = /requires = \[(.*)\]/.exec(pyproject); const pythonBuildRequires = hatch @@ -284,7 +291,7 @@ export function buildInventory(root) { }, provenance_workflow: { path: ".github/workflows/enterprise-slsa-provenance.yml", - attest_action: "actions/attest-build-provenance@v2", + attest_action: attest.uses, runs_on_push: publish["enterprise-slsa-provenance.yml"], formal_slsa_level: "NOT_CLAIMED", historical_log_contains_level_assertion: architecture.includes("SLSA Level"), diff --git a/scripts/release/ws11/ws11.test.mjs b/scripts/release/ws11/ws11.test.mjs index fd34cf2b..6dc939b4 100644 --- a/scripts/release/ws11/ws11.test.mjs +++ b/scripts/release/ws11/ws11.test.mjs @@ -8,7 +8,7 @@ import { spawnSync } from "node:child_process"; import { findForbiddenClaims, loadRequirements } from "./claims.mjs"; import { allGenerated, characterizeOptics } from "./characterize.mjs"; import { evaluateDossier, exitCodeFor } from "./evaluate.mjs"; -import { REPO_ROOT, buildSbom, privateManualPaths, readSealedPypi, scanManifestLicenses } from "./inventory.mjs"; +import { REPO_ROOT, actionUses, buildSbom, privateManualPaths, readSealedPypi, scanManifestLicenses } from "./inventory.mjs"; import { stableStringify } from "./stable.mjs"; import { assemblePeCustomerBundle, packageVersionProblems, readText } from "../../../docs/scripts/docs-release-lib.mjs"; @@ -262,13 +262,43 @@ test("workspace SBOM and license scan stay bounded to what the tree shows", () = assert.equal(scan.findings.some((item) => item.path === "packages/vantio-cli/package.json"), false); }); -test("pin report records floating actions and the sealed Python hashes", () => { +test("actionUses keeps SHA pins that carry a tag comment", () => { + const text = [ + " uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4", + " - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4", + " uses: actions/setup-node@v4", + " uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1", + ].join("\n"); + assert.deepEqual(actionUses(text), [ + "actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4", + "actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4", + "actions/setup-node@v4", + "pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1", + ]); +}); + +test("pin report records SHA-pinned workflow actions and the sealed Python hashes", () => { const { artifacts } = allGenerated(ROOT); const report = artifacts["pin-report.json"]; assert.equal(report.package_manager.name, "pnpm"); assert.equal(report.package_manager.version, "11.13.0"); assert.match(report.package_manager.integrity, /^sha512\./); - assert.equal(report.actions.every((action) => action.digest_pinned === false), true); + const floating = report.actions.filter((action) => action.digest_pinned === false); + const pinned = report.actions.filter((action) => action.digest_pinned === true); + assert.deepEqual( + floating.map((action) => `${action.file} ${action.uses}`), + [ + ".github/actions/vantio-prove/action.yml actions/setup-node@v4", + ".github/actions/vantio-prove/action.yml actions/upload-artifact@v4", + ], + ); + assert.ok(pinned.length > 0); + assert.equal(pinned.length, report.actions.length - floating.length); + assert.ok(pinned.every((action) => /@[0-9a-f]{40} # \S+$/.test(action.uses))); + assert.equal( + report.provenance_workflow.attest_action, + "actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2", + ); assert.equal(report.workflow_triggers_push["npm-publish.yml"], false); assert.equal(report.workflow_triggers_push["pypi-publish.yml"], false); assert.equal(report.workflow_triggers_push["mcp-registry-publish.yml"], false);