Repository navigation
ci: harden the release pipeline and dependency installs - #307
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…talls Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Codex Review: Didn't find any major issues. Breezy! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Codex Review: Didn't find any major issues. 🎉 Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
trustPolicyIgnoreAfter (30 days) applies the trust check to fresh releases, where hijacked versions appear, so the semver/vite exclusions are no longer needed. next@16.4.1 is now well past the one-day release age gate, so its minimumReleaseAgeExclude entries are removed. Also trims the comments. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review Review round 3 (1390f4d): replaced the
|
|
Codex Review: Didn't find any major issues. Swish! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Summary
Hardens the SDK release pipeline and dependency installs against the 2026 npm worm pattern (steal a token, or run inside a job with CI OIDC, then publish malicious versions) and against retargeted GitHub Action tags (the tj-actions/changed-files incident class). Also bumps
@edgestore/cli'ssmol-tomlpast two DoS advisories.1. Every action is pinned to a full commit SHA (
b6f766c0). A tag likechangesets/action@v1can be moved to new code by whoever controls that repo, and inrelease.ymlthat code runs withid-token: write, so it can publish@edgestore/*. Each pin is the commit the old tag or branch points to today, so behavior does not change:actions/checkout@v73d3c42e5…# v7.0.1actions/setup-node@v624997072…# v6.5.0pnpm/action-setup@v60977fd99…# v6.0.10changesets/action@v1(branch)a45c4d59…# v1.9.0actions/cache@v5caa29612…# v5.1.0actions/setup-go@v6924ae3a1…# v6.5.02. Dependabot keeps the pins current (
8f264bb1,314f04cb). It covers thegithub-actionsecosystem only and runs monthly. It waits 7 days after an action release before proposing it, the same idea as pnpm'sminimumReleaseAge. Minor and patch bumps come as one grouped PR. Major bumps (for examplechangesets/actionv2,setup-nodev7) each get their own PR, so a release-pipeline change is never buried in a batch.3. Less to steal or poison in CI (
7985dd2f,668a63aa):persist-credentials: falseon checkouts in jobs that never push: CI checks, the CLI smoke matrix, storage tests, and canary. Third-party test and build code in those jobs can no longer read theGITHUB_TOKENfrom.git/config. Jobs that push keep the default:release,maintenance,promote-next, andstart-rc. (changesets/action also writes its own~/.netrcfromGITHUB_TOKEN, so it doesn't depend on checkout credentials.)release,maintenance, andcanaryjobs no longer restore the pnpm store cache. Caches onmainare written by CI runs that execute third-party code. A publishing job shouldn't restore something those runs could have tampered with. Cost: a slower install on release runs.4. pnpm install policies (
6e3c24dd,pnpm-workspace.yaml):trustPolicy: no-downgradefails an install when a package version has weaker publish evidence than an earlier version (trusted publisher > provenance > none). This is what a hijacked maintainer token looks like. pnpm 11 checks both new resolutions and existing lockfile entries, so everypnpm install --frozen-lockfilein CI and release enforces it.blockExoticSubdepsisn't set: pnpm 11 already defaults it totrue, so only direct dependencies may come from git or tarball URLs.trustPolicyIgnoreAfter: 43200(30 days, in minutes): the trust check covers only releases from the last 30 days, which is where hijacked versions appear (they're usually caught and pulled within hours or days). Older legitimate releases with weaker evidence pass without a hand-maintained exclusion list. Today those aresemver@5.7.2,semver@6.3.1(2023 backports) andvite@5.4.21.minimumReleaseAgeExcludeentries fornext@16.4.1and its@next/*packages added in fix(deps): upgrade Next.js to 16.4.1 (October 14 security release) #304. That release is now well past the one-day gate.5.
@edgestore/cli:smol-toml1.7.0 → 1.9.1 (394fdedc, patch changeset). 1.9.1 is the latest version, published 2026-10-09, so it passes the 1-day release-age gate. It fixes GHSA-7w5x-hrqm-74c2 (a malformed document hangs the parser, fixed in 1.7.1) and GHSA-r4xh-jqrq-34v2 (quadratic-time parse, fixed in 1.9.0). The CLI parses Codex'sconfig.tomlwith it. The lockfile also deduped Astro's transitive copy to 1.9.1. The threedeprecated:lines that appeared for@opentelemetry/*entries are pnpm refreshing registry metadata.Release job split: evaluated, not done
The
releasejob runspnpm install,changeset version, andpnpm build(third-party build tools) in the same job that holdsid-token: write. Any step in that job can request an OIDC token and exchange it for npm publish tokens for@edgestore/*. Two things already limit this: pnpm runs no dependency lifecycle scripts (allowBuildsis allfalse), and installs are lockfile-frozen behind the release-age gate and, now, the trust policy.A real split would look like this:
I left the structure as it is because:
changesets/actiondoes version-PR-or-publish in one step. Its publish mode also decides which packages are unpublished, creates git tags, and creates GitHub Releases. A split means replacing that with custom scripts in three jobs (release,maintenance,canary). That isn't a simple change, and it can't be tested before merge.release.yml), plus an environment only if one is set. Publishing works today with noenvironment:on the job, so none is configured. A job split insiderelease.ymlkeeps working with that config. Adding an environment needs no npm change unless the npm config names it.A cheaper follow-up gets most of the benefit: split
releaseinto aversionjob withoutid-token, plus apublishjob (needs: version,if: hasChangesets == 'false') that keepschangesets/actionpublishing and runs in a protected GitHub environment with required reviewers. Most pushes tomainwould then never holdid-token, and every publish would wait for Ravi's approval.Evidence
gh api repos/<owner>/<repo>/git/ref/tags/<tag>, dereferencing annotated tags. All six match. I also resolved the old floating refs (v7,v6,v5, and thechangesets/actionv1branch). Each points to the same pinned commit, so pinning changes no code that runs..github/workflows/*passes on this branch and onmain.dependabot-2.0schema. All workflow YAML parses in strict mode.pnpm install --lockfile-onlyfails withERR_PNPM_TRUST_DOWNGRADEonsemver@5.7.2,semver@6.3.1andvite@5.4.21, so the check runs against the lockfile. WithtrustPolicyIgnoreAfter: 43200, and the Next.js release-age exclusions removed, it reports✓ Lockfile passes supply-chain policies (1746 entries)and leaves the lockfile unchanged.pnpm --filter @edgestore/cli test: 29 files, 390 tests passed.pnpm --filter @edgestore/cli typecheckandpnpm check:dependenciespass.release.ymlitself. The diff there only changesuses:refs to the same commits and removescache: pnpmfrom threesetup-nodesteps. The first push tomainafter merge runs it in version-PR mode. The first publish runs after the next Version Packages PR is merged.Risk and rollback
Door: Two-way. Revert the PR to restore floating tags, caching, and the old pnpm settings. The CLI change ships in the next release as a patch. Because the
@edgestore/*packages are versioned together (fixed), that release bumps all of them.Blast radius:
pnpm install(contributors and CI): it now fails if a dependency update brings a release from the last 30 days with weaker publish evidence. Check who published it first, because a real hijack looks exactly like this. If it's legitimate, add an exact-versiontrustPolicyExcludeentry or wait until it's 30 days old. Don't relax the policy.setup-nodev7,changesets/actionv2,cachev6,setup-gov7). Review thechangesets/actionv2 PR carefully before merging.@edgestore/cliusers get a newer TOML parser. The parse API is unchanged.🤖 Generated with Claude Code