Skip to content

VOID v0.6 public baseline #1

VOID v0.6 public baseline

VOID v0.6 public baseline #1

name: CVE Appendix (per-release snapshot)
# Per-release, lockfile-complete CVE appendix (Task #255).
#
# `docs/security-audit-cve-appendix.md` is the public per-release snapshot
# of every advisory `pnpm audit --json --audit-level=info` returns against
# the lockfile. The committed file embeds the lockfile sha256 and the
# commit hash that last touched the lockfile (deterministic, stable across
# unrelated commits).
#
# Three jobs:
# 1. `enforce-current` — on PRs that touch the lockfile / appendix /
# parser, regenerate in `--check` mode and fail on drift. The PR
# author runs `node scripts/regen-cve-appendix.mjs` locally and
# commits the regenerated file.
# 2. `regen-and-commit-on-main` — on `main` pushes that touch the
# lockfile, regenerate the appendix and commit it directly to
# `main` (only when the appendix actually changed). This means the
# next release tag is taken from a commit whose tree already
# contains a current appendix; no tag rewrite is needed.
# 3. `attach-to-release` — on `v*` tag pushes, verify the committed
# appendix is current for the tagged tree (`--check`) and upload a
# tag-stamped copy as an immutable GitHub Release asset. Tags are
# never force-pushed.
#
# Division of labor with Task #254:
# - Task #254 workflow: build-fail on release branch, daily scheduled
# scan that opens / dedupes / closes operator-private issues.
# - This workflow: per-release static markdown snapshot, committed
# to the tag's tree and attached to the GitHub Release as an asset.
# Both reuse `scripts/lib/pnpm-audit-parser.mjs` — same JSON-shape
# handling, same pnpm-version pin, same severity classifier.
on:
pull_request:
branches: [main]
paths:
- "pnpm-lock.yaml"
- "docs/security-audit-cve-appendix.md"
- "scripts/regen-cve-appendix.mjs"
- "scripts/lib/pnpm-audit-parser.mjs"
- ".github/workflows/cve-appendix-release.yml"
push:
branches: [main]
paths:
- "pnpm-lock.yaml"
- "scripts/regen-cve-appendix.mjs"
- "scripts/lib/pnpm-audit-parser.mjs"
tags:
- "v*"
workflow_dispatch:
permissions:
contents: read
jobs:
enforce-current:
name: Enforce appendix is current for the lockfile (PRs)
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up pnpm
# Keep this version in sync with PNPM_PINNED_MAJOR in
# scripts/lib/pnpm-audit-parser.mjs. The parser will refuse to
# run against a different pnpm major to prevent silent
# JSON-shape drift between this snapshot path and the Task #254
# monitoring path that imports the same module.
uses: pnpm/action-setup@v4
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- name: Install dependencies (frozen lockfile)
run: pnpm install --frozen-lockfile
- name: Verify appendix is current
run: node scripts/regen-cve-appendix.mjs --check
- name: Gate on unmapped High/Critical advisories
# Strict release gate: fail the PR if any High/Critical advisory has
# no audit-ledger entry, so a fresh High/Critical cannot merge toward
# a release tag until it is mapped into AUDIT_LEDGER / the audit doc.
run: node scripts/regen-cve-appendix.mjs --strict
regen-and-commit-on-main:
name: Regenerate appendix and commit on main
# Canonical-repo only: this job commits back to `main`. A fork inherits the
# workflow but must not get bot commits pushed into it. `fork == false` is
# true on the canonical repo and is populated for push events.
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && github.event.repository.fork == false
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
steps:
- name: Checkout main
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up pnpm
uses: pnpm/action-setup@v4
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- name: Install dependencies (frozen lockfile)
run: pnpm install --frozen-lockfile
- name: Regenerate appendix
run: node scripts/regen-cve-appendix.mjs
- name: Commit if changed
run: |
set -euo pipefail
if git diff --quiet -- docs/security-audit-cve-appendix.md; then
echo "Appendix already current; nothing to commit."
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add docs/security-audit-cve-appendix.md
git commit -m "Regenerate CVE appendix for $(git rev-parse --short HEAD)"
git push origin HEAD:main
attach-to-release:
name: Attach CVE appendix to GitHub Release
# Canonical-repo only: this job creates/uploads GitHub Releases. Forks
# inherit the workflow but must not publish releases. `fork == false` is
# true on the canonical repo and is populated for tag-push events.
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') && github.event.repository.fork == false
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
steps:
- name: Checkout tag
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up pnpm
uses: pnpm/action-setup@v4
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- name: Install dependencies (frozen lockfile)
run: pnpm install --frozen-lockfile
- name: Verify committed appendix matches the tagged lockfile
# The committed file is deterministic from the lockfile sha256
# plus the lockfile-touching commit hash, so this check is
# stable for the tagged tree.
run: node scripts/regen-cve-appendix.mjs --check
- name: Gate on unmapped High/Critical advisories
# Strict release gate: block the tag from publishing if any
# High/Critical advisory has no audit-ledger entry. This runs before
# any Release is created/uploaded, so a non-zero exit here aborts the
# job and the GitHub Release asset is never attached.
run: node scripts/regen-cve-appendix.mjs --strict
- name: Render Release-asset copy with explicit tag
# The asset copy embeds the tag in the `Release` field. The
# committed file is left untouched.
run: |
mkdir -p .release-artifacts
node scripts/regen-cve-appendix.mjs \
--stdout --release="${GITHUB_REF_NAME}" \
> .release-artifacts/security-audit-cve-appendix.md
- name: Upload appendix as Release asset
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
# Tag is the source of truth; we never rewrite it. We attach
# the tag-stamped appendix as an immutable Release asset so
# operators can download "the appendix as it stood at release
# time" without having to clone the tag.
#
# If a Release for this tag does not yet exist (e.g. the
# tag was pushed without a corresponding Release being
# created elsewhere), create a draft so the upload has a
# target. A maintainer can then publish the draft with the
# appendix already attached.
if ! gh release view "${GITHUB_REF_NAME}" >/dev/null 2>&1; then
gh release create "${GITHUB_REF_NAME}" \
--draft \
--title "${GITHUB_REF_NAME}" \
--notes "Auto-created by cve-appendix-release workflow to attach the lockfile-complete CVE appendix. Edit and publish when ready."
fi
gh release upload "${GITHUB_REF_NAME}" \
.release-artifacts/security-audit-cve-appendix.md \
--clobber