Supply Chain Security skill for Codex
Install what you reviewed, ship what you can prove. Codex works through supply chain controls in the order that stops real attacks: frozen lockfile installs, install scripts off by default, explicit registry scoping against dependency confusion, SHA-pinned GitHub Actions and release cooldowns. It then adds SBOMs, keyless cosign signatures and GitHub artifact attestations, and wires verification into the deploy path so signing actually protects something.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- supply-chain-security Installs to ~/.agents/skills/supply-chain-security/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install supply-chain-security The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project supply-chain-security Installs to .agents/skills/supply-chain-security/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/supply-chain-security.zipunzip -o supply-chain-security.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) supply-chain-security PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
104-line POSIX script. No sudo, no telemetry. Read install.sh
Use it in Codex
Start a prompt with $supply-chain-security, or pick it from /skills.
You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.
What Codex reads before loading it
Hardens a project's software supply chain - committed lockfiles and frozen installs, install-script controls, dependency confusion and typosquat defence, pinned GitHub Actions, SBOM generation with Syft or cdxgen, artifact signing and verification with Sigstore cosign, SLSA build provenance and GitHub artifact attestations, npm trusted publishing and OpenSSF Scorecard. Use when securing builds and releases, publishing packages, responding to a compromised dependency, or meeting SBOM and provenance requirements.
Use it when
- Securing build, CI and release pipelines against malicious dependencies
- A customer or regulation asks for SBOMs or build provenance
- Publishing packages to npm or PyPI
- A dependency you use was compromised
Use something else when
- You only need to find known CVEs in dependencies: use dependency-scanning
- You are scanning container images for vulnerabilities: use container-scanning
- Your focus is GitHub Actions workflow permissions and injection: use github-actions-security
How to check the result
CI logs show frozen installs without unapproved lifecycle scripts, all third-party actions are SHA-pinned, and cosign verify plus gh attestation verify pass for the release digest but fail for an unsigned build.
The mistake it prevents
Signing artifacts in CI but never verifying signatures or provenance before deploying, so a tampered image runs anyway.
Done well means: every build installs exactly the dependencies that were reviewed, from the registries you intend, without running arbitrary install scripts; every released artifact has an SBOM and verifiable provenance tying it to a commit and workflow; and consumers (including your own deploy step) verify that before running it.
Inspect first
ls package-lock.json pnpm-lock.yaml yarn.lock bun.lock uv.lock poetry.lock requirements*.txt go.sum Cargo.lock Gemfile.lock 2>/dev/null
cat .npmrc .yarnrc.yml pnpm-workspace.yaml pip.conf 2>/dev/null
rg -n "uses: [^@]+@" .github/workflows | rg -v "@[0-9a-f]{40}" # actions not pinned to a SHA
rg -n "npm install|pip install|curl .*\| *(sh|bash)" .github/workflows Dockerfile* Makefile 2>/dev/null
Read AGENTS.md for the package manager and release process. Most steps below need
network (registries, Sigstore, GitHub); in a sandbox with network off, prepare the
config and ask for approval to run the networked parts.
Priorities
Do these in order; each is cheap and closes a common real-world attack:
| # | Control | Stops |
|---|---|---|
| 1 | Lockfile committed; CI uses frozen installs | Silent version drift, compromised new releases |
| 2 | Install scripts off by default, allowlisted | Postinstall malware (the main npm worm vector) |
| 3 | Scoped/private packages pinned to the right registry | Dependency confusion |
| 4 | GitHub Actions pinned to full commit SHAs, minimal permissions | Compromised or retagged actions |
| 5 | Release delay for new versions (cooldown) | Freshly published malicious versions |
| 6 | SBOM per release | Unknown inventory during the next CVE scramble |
| 7 | Signed artifacts + provenance, verified at deploy | Tampered builds, rogue publishes |
| 8 | Trusted publishing (OIDC) instead of long-lived registry tokens | Stolen npm/PyPI tokens |
1-3. Install hygiene
| Ecosystem | Frozen install | Script control |
|---|---|---|
| npm | npm ci | npm ci --ignore-scripts, then run needed builds explicitly; or ignore-scripts=true in .npmrc |
| pnpm 10+ | pnpm install --frozen-lockfile | Dependency lifecycle scripts are off by default; allow specific packages with onlyBuiltDependencies |
| Yarn Berry | yarn install --immutable | enableScripts: false in .yarnrc.yml, per-package dependenciesMeta |
| Bun | bun install --frozen-lockfile | Scripts run only for trustedDependencies |
| uv | uv sync --locked | Prefer wheels; --no-build refuses source builds |
| pip | pip install --require-hashes -r requirements.txt | --only-binary :all: where feasible |
| Go | go mod download + go mod verify | Checksum DB (GOSUMDB) on; GOFLAGS=-mod=readonly |
| Cargo | cargo build --locked | Review build.rs in new deps |
Dependency confusion: map internal scopes to the internal registry explicitly
(@acme:registry=https://npm.acme.internal/ in .npmrc), never rely on fallback
ordering across registries, and reserve your internal package names on the public
registry where possible. For pip, avoid --extra-index-url mixing public and private
indexes; use a single proxying index or uv’s per-package index pinning.
Cooldowns: Renovate minimumReleaseAge (e.g. "3 days"), pnpm minimumReleaseAge
(minutes) and uv exclude-newer delay adoption of brand-new versions, which is when most
malicious releases are caught and pulled.
4. CI hardening
permissions:
contents: read # default to read-only; grant per job
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@<40-char-sha> # v4.2.2
with:
persist-credentials: false
- Pin third-party actions to SHAs with the version in a comment; let Dependabot or Renovate update them.
- Never run
pull_request_targetworkflows that check out and execute PR code. - Keep publish credentials in a separate job or environment with required reviewers.
- Deeper workflow review:
$github-actions-security.
6. SBOM
syft dir:. -o cyclonedx-json=sbom.cdx.json # source tree
syft ghcr.io/acme/api@sha256:<digest> -o spdx-json=sbom.spdx.json # image
npx @cyclonedx/cdxgen -o sbom.cdx.json # alternative, language-aware
Generate from the built artifact (image or package), not only from the source tree, so
the SBOM reflects what ships. Store it with the release and attach it as an attestation.
Scan it for vulnerabilities with grype sbom:sbom.cdx.json or osv-scanner
(see $dependency-scanning).
7. Signing and provenance
Keyless signing with Sigstore in GitHub Actions (needs id-token: write):
permissions:
contents: read
packages: write
id-token: write
attestations: write
steps:
- uses: sigstore/cosign-installer@<sha>
- run: cosign sign --yes ghcr.io/acme/api@${{ steps.build.outputs.digest }}
- uses: actions/attest-build-provenance@<sha>
with:
subject-name: ghcr.io/acme/api
subject-digest: ${{ steps.build.outputs.digest }}
push-to-registry: true
Verify before deploy (this is the step people skip):
cosign verify ghcr.io/acme/api@sha256:<digest> \
--certificate-identity-regexp '^https://github.com/acme/api/.github/workflows/release.yml@refs/tags/v' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
gh attestation verify oci://ghcr.io/acme/api@sha256:<digest> --owner acme
Always sign and deploy by digest, never by tag. In Kubernetes, enforce verification with
an admission policy (Sigstore policy-controller or Kyverno verifyImages).
SLSA: GitHub artifact attestations give SLSA Build Level 2 provenance; reusable, isolated build workflows move toward Level 3. State the level you actually meet, not the one you aim for.
8. Publishing packages
- npm: trusted publishing via OIDC from GitHub Actions or GitLab CI (configured on
npmjs.com per package) removes long-lived
NPM_TOKENs and adds provenance automatically. Otherwisenpm publish --provenance. - PyPI: trusted publishers with
pypa/gh-action-pypi-publish; attestations are produced automatically. - Require 2FA for all maintainers; restrict who can publish.
- Publishing is outward-facing and irreversible in practice: never publish or change registry settings without explicit user confirmation.
Consumers can check registry signatures and provenance: npm audit signatures.
Responding to a compromised dependency
- [ ] Identify affected versions and whether they are in any lockfile or image (SBOM search)
- [ ] Pin to a known-good version; rebuild with frozen install
- [ ] If the malicious version was installed anywhere (CI runners, dev machines),
treat those hosts' credentials as exposed: rotate CI secrets, npm/GitHub tokens, cloud keys
- [ ] Check for persistence the payload is known to create (workflows added to repos, new tokens)
- [ ] Hand off to $incident-response if credentials were exposed
Verification
- CI log shows frozen install and no lifecycle scripts outside the allowlist.
rgover workflows finds no unpinned third-party actions.cosign verifyandgh attestation verifysucceed for the latest release digest and fail for an unsigned local build.- OpenSSF Scorecard (
scorecard --repo=github.com/acme/api, needs a GitHub token) shows improvement in Pinned-Dependencies, Token-Permissions and Signed-Releases.
Output template
## Supply chain review: <repo>
| Control | Status | Evidence / change |
|---|---|---|
| Frozen installs in CI | yes | npm ci in build.yml:22 |
| Install scripts | allowlisted | .npmrc ignore-scripts + explicit rebuild of esbuild |
| Actions pinned | 3 of 9 -> 9 of 9 | |
| SBOM | added | release.yml, CycloneDX |
| Signing / provenance | added, verified at deploy | |
Residual risk: <unreviewed transitive deps, build runner isolation>
Gotchas
npm installin CI rewrites the lockfile when ranges allow; onlynpm cifails on drift.- Signing without verification is theatre. Add the verify step to the deploy path.
- Tags are mutable in container registries and Git; pin and sign digests and SHAs.
- Self-hosted runners shared between repos let one compromised job poison the next.
What the zip contains
SKILL.md plus the interface file below. Nothing runs on install.
agents/openai.yaml
Gives the Codex app a display name, a one-line description and a starter prompt for this skill.
interface:
display_name: "Supply Chain Security"
short_description: "Install what you reviewed, ship what you can prove"
default_prompt: "$supply-chain-security Harden our npm supply chain after the latest worm news" About this skill
Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.