Skip to content

ci: credential-free release build, SHA-pinned actions, docs site headers - #86

Merged
ExorTek merged 5 commits into
masterfrom
ci/repo-supply-chain-hardening
Oct 25, 2026
Merged

ExorTek merged 5 commits into
masterfrom
ci/repo-supply-chain-hardening

Conversation

@ExorTek

@ExorTek ExorTek commented Oct 25, 2026

Copy link
Copy Markdown
Owner

Summary

Release workflow. The release job held id-token: write while it ran yarn install, so every dependency's lifecycle scripts executed next to the npm OIDC token. It also carried contents: write, which nothing uses: tags and GitHub Releases come from scripts/releases.sh. The workflow is now two jobs:

  • build
    • Installs, builds and uploads dist/ as an artifact.
    • Has a read-only token and no id-token.
    • A dry run also prints every tarball's contents.
  • publish
    • Runs only for a real release, inside the npm environment. Configure that environment with required reviewers so each publish needs approval.
    • Installs with --mode=skip-build, so no dependency script runs, then publishes the uploaded dist/ with provenance as before.

Action pinning. Every action in CI, CodeQL and Release is pinned to a commit SHA, with the tag in a comment. Dependabot's github-actions updater keeps the pins current. Checkouts no longer persist the token.

Docs site headers. HSTS and nosniff already come from Cloudflare. This adds:

  • a CSP limited to directives that cannot break Next's inline scripts or the analytics beacon: frame-ancestors, base-uri, object-src and form-action;
  • X-Frame-Options, Referrer-Policy and Permissions-Policy.

Docs dependencies. @xmldom/xmldom is pinned to 0.9.12 through resolutions. speech-rule-engine pinned 0.9.10 exactly, which has several high-severity advisories.

AGENTS.md. The "only sanctioned cross-package edges" list now includes the edges that oauth2, oidc and the auth umbrella actually declare.

Considered and not changed

  • SRI on the Cloudflare beacon: the URL is unversioned, so a pinned hash would break on every update.
  • /og query text: already capped (title 80 characters, description 150).
  • katex below 0.18.2 (low): nextra, mermaid and micromark-extension-math all require ^0.16, and the site only renders math we write ourselves.
  • mathjax-full 3.2.2 (moderate): no fixed release exists.

Test plan

  • Local yarn workspaces foreach --all --no-private --verbose pack --dry-run lists dist/, LICENSE, README and CHANGELOG for every package.
  • Docs site: yarn build passes; next start serves the four headers on /, /jwt and /og; the beacon script is still on the page.
  • yarn npm audit --all --recursive: only katex (low) and mathjax-full (moderate) remain.
  • Release workflow dry run on this branch (gh workflow run release.yml --ref ci/repo-supply-chain-hardening -f dry_run=true).

…ted environment

The single release job held id-token: write while running yarn install,
so every dependency's lifecycle scripts executed next to the npm OIDC
token, and it carried contents: write that nothing uses any more (tags
and GitHub Releases come from scripts/releases.sh).

Split into two jobs. build installs, builds and uploads dist/ with a
read-only token and no id-token; a dry run also prints every tarball's
contents. publish runs only for a real release, in the protected npm
environment, installs with --mode=skip-build so no dependency script
runs, and publishes the uploaded dist/. Checkouts no longer persist
credentials.
Every action was referenced by a movable tag, so a compromised or
re-pointed tag would run new code in CI and in the release job. Pin
each to the commit its current tag resolves to, with the tag in a
comment for readability, and stop persisting the checkout token in
the CI and CodeQL jobs, which never push.
The site relied on Cloudflare's HSTS and nosniff only. Add a CSP
limited to directives that cannot break Next's inline hydration scripts
or the analytics beacon (frame-ancestors, base-uri, object-src,
form-action), X-Frame-Options for older browsers, Referrer-Policy and a
Permissions-Policy that denies device APIs the docs never use.
speech-rule-engine (mathjax, pulled in by nextra) pins @xmldom/xmldom
0.9.10 exactly, which carries several high-severity advisories fixed in
0.9.12. Force the patched version through resolutions; the docs build
is unchanged.

Not changed: katex <0.18.2 (low) — nextra, mermaid and
micromark-extension-math all require ^0.16 and the site only renders
math it authors itself; mathjax-full 3.2.2 (moderate) has no fixed
release.
AGENTS.md called its list the only sanctioned runtime edges but left
out the ones oauth2, oidc and the auth umbrella declare, so the rule
contradicted the package.json files it describes.
Copilot AI balanced review requested due to automatic review settings October 25, 2026 07:21

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@ExorTek
ExorTek merged commit 5aba22a into master Oct 25, 2026
3 checks passed
@ExorTek
ExorTek deleted the ci/repo-supply-chain-hardening branch October 25, 2026 07:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants