Skip to content
Security
Skill

/stash-supply-chain-security

Supply-chain security controls for the @cipherstash/stack monorepo. Covers post-install script policy (onlyBuiltDependencies), install cooldown (minimumReleaseAge), lockfile integrity (blockExoticSubdeps + lockfile registry check), frozen-lockfile CI, registry pinning (.npmrc),

BOOST
From plugin
stack
16116 skills1 command
Install
$ npx -y skills add cipherstash/stack --skill stash-supply-chain-security --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/stash-supply-chain-security

Context preview

The summary Claude sees to decide when to auto-load this skill.

Supply-chain security controls for the @cipherstash/stack monorepo. Covers post-install script policy (onlyBuiltDependencies), install cooldown (minimumReleaseAge), lockfile integrity (blockExoticSubdeps + lockfile registry check), frozen-lockfile CI, registry pinning (.npmrc),

SKILL.md

stash-supply-chain-security.SKILL.md
name: stash-supply-chain-security
description: Supply-chain security controls for the @cipherstash/stack monorepo. Covers post-install script policy (onlyBuiltDependencies), install cooldown (minimumReleaseAge), lockfile integrity (blockExoticSubdeps + lockfile registry check), frozen-lockfile CI, registry pinning (.npmrc), Dependabot cooldown, CODEOWNERS, and npm OIDC trusted publishing / provenance (including claiming a new package name). Use when modifying CI workflows, pnpm config, dependency updates, .github/dependabot.yml, release.yml, publishing a package to npm for the first time, or anything that touches how packages enter the build.

Supply Chain Security

Controls applied in this repo to limit blast radius from compromised npm packages, lockfile injection, dependency confusion, and rushed dependency upgrades. Sourced from [lirantal/npm-security-best-practices](https://github.com/lirantal/npm-security-best-practices) and adapted for our pnpm workspace.

When to Use This Skill

  • Modifying any file under `.github/workflows/`
  • Editing `pnpm-workspace.yaml`, `package.json` `pnpm` block, or `.npmrc`
  • Updating `.github/dependabot.yml` or `.github/CODEOWNERS`
  • Adding a dependency that needs a build script (i.e. `node-gyp`, `node-pty`, prebuilt binaries)
  • Bypassing the install cooldown for a security fix
  • Publishing a package to npm under a name that has never been published before
  • Reviewing a PR that touches any of the above

What's Enforced (Config + Test Gate)

Each control below is validated by `e2e/tests/supply-chain.e2e.test.ts` — the test suite fails CI if a control regresses, so silent removal isn't possible.

1. Post-install scripts disabled by default — practice #1

pnpm 10+ disables lifecycle scripts globally and only runs them for packages on the `onlyBuiltDependencies` allowlist.

  • **Where**: `package.json` `pnpm.onlyBuiltDependencies`
  • **Current allowlist**: `["node-pty"]` (PTY tests need the native module built)
  • **Test asserts**: allowlist length ≤ 3 — adding a fourth entry forces explicit review

2. Install cooldown — practice #2

New package versions wait 7 days before they're eligible for install. Mirrors the Dependabot cooldown so manual + automated updates have the same community-discovery window.

  • **Where**: `pnpm-workspace.yaml` `minimumReleaseAge: 10080` (minutes)
  • **Test asserts**: ≥ 4320 minutes (3 days)

3. Lockfile injection prevented — practices #4, #16

Two layers:

  • `pnpm-workspace.yaml` `blockExoticSubdeps: true` — pnpm refuses to install transitive deps that come from git or direct tarballs (pnpm ≥ 10.26)
  • A test parses `pnpm-lock.yaml` and asserts every resolved tarball URL starts with `https://registry.npmjs.org/`

(Why not `lockfile-lint`? It only supports npm/yarn lockfiles. The pnpm-native test gives us the same protection.)

4. Frozen lockfile in CI — practice #5

CI uses `pnpm install --frozen-lockfile`. If `pnpm-lock.yaml` and any `package.json` drift, the install aborts — no silent registry fetches that bypass the locked versions.

  • **Where**: every workflow under `.github/workflows/`, and every local composite action under `.github/actions/`
  • **Test asserts**: no `pnpm install` step anywhere in that graph is missing `--frozen-lockfile`

The check used to read `tests.yml` alone, and `release.yml` — the one workflow that publishes to npm — ran a bare `pnpm install` the whole time. The single install allowed to resolve outside the lockfile was the one whose output goes to the registry.

A pip requirements file installed in CI is held to the same bar: pin every package, transitive ones included, with `==` and a `--hash`, and install it with `pip install --require-hashes --no-deps -r <file>`, so nothing resolves at install time. `uv pip compile --generate-hashes` produces such a file. (This one is checked by the workflow's own test, not by `supply-chain.e2e.test.ts`.)

5. Cooldown'd auto-updates — practice #6

Dependabot opens grouped, cooldown'd PRs (7 days minor/patch) for `npm`, `cargo`, `gomod`, `pip` (the hashed requirements file for a Python tool CI runs), `github-actions` and `docker` (the digest of the Alpine image the musl binaries are built in). Major bumps are not proposed at all — every entry ignores `version-update:semver-major`, so majors are reviewed and applied by hand.

There is deliberately **no `semver-major-days` cooldown** on any entry. It would delay major *version update* PRs, which the `ignore` above means Dependabot never opens, and cooldown does not reach the security path either ("the cooldown option is only available for version updates, not security updates"). Don't add one back as a safety net for the day the `ignore` is dropped — dead config reads as policy, and the test below fails on the pair.

`cargo` covers the in-tree Rust workspace at `languages/typescript/packages/protect-ffi` (**not** the repo root — that is where `Cargo.toml`/`Cargo.lock` live). It runs monthly rather than weekly because each bump costs a native rebuild to validate, and it ignores the exact-pinned CipherStash crates (`cipherstash-client`, `cts-common`, `stack-auth`, `stack-profile`, `eql-bindings`, `vitaminc`) — they share a release train with the `@cipherstash/auth` catalog and must be bumped together, manually.

  • **Where**: `.github/dependabot.yml`
  • **Test asserts**: cooldown ≥ 3 days on every entry; the sentence above names every monitored ecosystem; every entry ignores `version-update:semver-major` for `*` **and** sets no `semver-major-days` (both ends, so neither half can drift alone); every lockfile present in the repo maps to a monitored `package-ecosystem`; every entry's `directory` actually contains the manifest its ecosystem reads

The ecosystem-coverage assertion is derived from the filesystem, so **adding a lockfile for a new language fails the suite until `dependabot.yml` covers it.** Two lockfiles are exempt because Dependabot has no ecosystem for them (`e2e/wasm/deno.lock`

Read more
Ships withstack

Searchable, application-level encryption for building privacy-first apps

Get the whole plugin

Other skills on stack.

stash-auth
Skill

stash-auth

How a CipherStash client authenticates — the canonical skill for credentials, auth strategies, and lock context. Covers the…

stash-cli
Skill

stash-cli

Drive CipherStash setup and encryption migrations through the `stash` CLI — `init`, `plan`, `impl`, `status`, `auth login`, `eql…