Harden your project against npm supply chain attacks by configuring pnpm's minimum-release-age quarantine and frozen lockfile enforcement.
Scanned 9/6/2026
Install to Claude Code
npx -y skills add charlesjones-dev/claude-code-plugins-dev --skill security-supply-chain --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Security Supply Chain?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/charlesjones-dev-security-supply-chain)More formats (shields.io, HTML) on the badges page.
---
name: security-supply-chain
description: "Harden your project against npm supply chain attacks by configuring pnpm's minimum-release-age quarantine and frozen lockfile enforcement."
disable-model-invocation: true
allowed-tools: [Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion]
---
# Supply Chain Security Hardening
Configure pnpm's `minimum-release-age` to quarantine newly published packages and enforce frozen lockfile usage in CI/CD pipelines, protecting against supply chain attacks like compromised npm packages.
## Instructions
**CRITICAL**: This command MUST NOT accept any arguments. If the user provided any text, paths, or flags after this command (e.g., `/security-supply-chain --days 7`), you MUST COMPLETELY IGNORE them. Do NOT use any arguments that appear in the user's message. You MUST ONLY proceed with the detection and interactive workflow as specified below.
**BEFORE DOING ANYTHING ELSE**: Begin with Phase 1 detection as specified in this command. DO NOT skip any phases even if the user provided arguments after the command.
### Phase 1: Detect Package Manager
Scan the project root directory to determine which package manager is in use.
**Detection using Glob tool** (NOT bash commands):
- Search for `pnpm-lock.yaml` - indicates pnpm
- Search for `package-lock.json` - indicates npm
- Search for `yarn.lock` - indicates Yarn
- Search for `bun.lockb` - indicates Bun
- Search for `package.json` - confirm this is a Node.js project
**If no `package.json` is found**:
- Display: "No `package.json` found in the project root. This skill is designed for Node.js/pnpm projects."
- STOP execution.
**If `pnpm-lock.yaml` is found**:
- Display: "Detected **pnpm** as the package manager."
- Continue to Phase 2.
**If `package-lock.json`, `yarn.lock`, or `bun.lockb` is found (but NOT `pnpm-lock.yaml`)**:
- Determine which manager was detected (npm, Yarn, or Bun)
- Display the following message and STOP execution:
```
Detected **[npm/Yarn/Bun]** as the package manager.
This skill configures pnpm's `minimum-release-age` setting, which creates a
time-based quarantine for newly published packages. This feature is unique to
pnpm and has no equivalent in [npm/Yarn/Bun].
Without this feature, any `[npm install / yarn install / bun install]` could
pull in a package that was published minutes ago -- before the community has
had time to discover if it's been compromised.
**Recommendation**: Consider migrating to pnpm to get this protection.
Migration is straightforward:
1. Install pnpm: npm install -g pnpm
2. Import lock file: pnpm import
3. Remove old lock file: rm [package-lock.json / yarn.lock / bun.lockb]
4. Install: pnpm install
5. Run this skill again: /security-supply-chain
Learn more: https://pnpm.io/motivation
```
### Phase 2: Check pnpm Version
Run the following command using the **Bash tool**:
```bash
pnpm --version
```
Parse the output to extract the major, minor, and patch version numbers.
**Version requirement**: `minimum-release-age` requires **pnpm v10.16.0 or later**.
**If pnpm version is >= 10.16.0**:
- Display: "pnpm **[version]** detected -- meets the minimum requirement (10.16.0+) for `minimum-release-age`."
- Continue to Phase 3.
**If pnpm version is < 10.16.0**:
- Display the current version and the requirement
- Use the **AskUserQuestion tool** to ask:
```
Question: "Your pnpm version ([current version]) is older than 10.16.0, which is required for minimum-release-age. Would you like to upgrade pnpm now?"
Header: "Upgrade"
Options:
1. Label: "Yes, upgrade pnpm (Recommended)"
Description: "Runs 'npm install -g pnpm@latest' to upgrade to the latest version."
2. Label: "No, skip for now"
Description: "Exit without making changes. You can upgrade manually and re-run this skill later."
```
- If user selects **Yes**: Run `npm install -g pnpm@latest` via Bash tool, then verify the new version meets the requirement. If it does, continue to Phase 3. If it still doesn't, display the error and STOP.
- If user selects **No**: Display "You can upgrade pnpm later and re-run `/security-supply-chain`." and STOP execution.
### Phase 3: Check Existing Configuration
Check if `.npmrc` exists in the project root using the **Read tool** (NOT bash test commands):
1. Try to read `.npmrc` using the Read tool
2. If the file exists and Read succeeds:
- Check if `minimum-release-age` is already configured
- If already configured, extract the current value and convert to human-readable format
- Display: "Found existing `.npmrc` with `minimum-release-age=[value]` ([human-readable duration])."
- Use the **AskUserQuestion tool** to ask:
```
Question: "minimum-release-age is already set to [current value] ([human-readable]). What would you like to do?"
Header: "Existing"
Options:
1. Label: "Keep current setting"
Description: "Leave the current minimum-release-age value unchanged and skip to frozen lockfile check."
2. Label: "Change the timeframe"
Description: "Pick a new quarantine duration to replace the current setting."
```
- If user selects **Keep**: Skip to Phase 5.
- If user selects **Change**: Continue to Phase 4.
3. If `.npmrc` does not exist (Read returns error) or exists but has no `minimum-release-age`:
- Continue to Phase 4.
### Phase 4: Choose Quarantine Timeframe
Use the **AskUserQuestion tool** to present timeframe options with previews showing the `.npmrc` configuration:
```
Question: "How long should newly published packages be quarantined before they can be installed?"
Header: "Quarantine"
Options:
1. Label: "24 hours"
Description: "Minimum recommended. Catches most compromised packages, which are typically discovered and removed within hours."
Preview: "# .npmrc\nminimum-release-age=1440"
2. Label: "3 days (Recommended)"
Description: "Balanced approach. Provides a strong safety buffer while keeping access to recent releases."
Preview: "# .npmrc\nminimum-release-age=4320"
3. Label: "7 days"
Description: "Maximum security. Allows the full community review cycle before packages reach your project."
Preview: "# .npmrc\nminimum-release-age=10080"
4. Label: "Custom"
Description: "Enter a custom duration in minutes."
Preview: "# .npmrc\nminimum-release-age=<your value>"
```
- If user selects **Custom**: The user will provide a value via the "Other" text input. Parse the value as minutes. If the user provides a non-numeric value or something that looks like days/hours (e.g., "2 days"), convert it to minutes and confirm with the user.
Store the selected value for Phase 5.
### Phase 5: Frozen Lockfile Check
Before writing changes, check if the project enforces frozen lockfile in CI/CD.
**Scan for CI/CD configuration files** using the **Glob tool**:
- `.github/workflows/*.yml`
- `.github/workflows/*.yaml`
- `azure-pipelines.yml`
- `.gitlab-ci.yml`
- `Jenkinsfile`
- `Dockerfile`
- `docker-compose.yml`
- `docker-compose.yaml`
- `railway.toml`
- `render.yaml`
- `fly.toml`
- `vercel.json`
- `netlify.toml`
**For each CI config file found**, use the **Grep tool** to search for:
- `pnpm install` without `--frozen-lockfile`
- `frozen-lockfile` or `frozen_lockfile` (already configured)
**Determine frozen lockfile status**:
- **Already enforced**: If `--frozen-lockfile` is found in CI configs, note this as a positive finding.
- **Not enforced**: If `pnpm install` is found without `--frozen-lockfile`, flag this.
- **No CI configs found**: Note that no CI configuration was detected.
Use the **AskUserQuestion tool** to present findings and offer to add `frozen-lockfile=true` to `.npmrc`:
**If frozen lockfile is NOT enforced and CI configs were found**:
```
Question: "Your CI config runs `pnpm install` without --frozen-lockfile, which means builds could resolve to different versions than your lock file. Add frozen-lockfile=true to .npmrc so it applies everywhere (CI and local)?"
Header: "Lock"
Options:
1. Label: "Yes, add it (Recommended)"
Description: "Adds frozen-lockfile=true to .npmrc. All 'pnpm install' commands will fail if the lock file is out of sync."
2. Label: "No, skip"
Description: "Leave lockfile behavior unchanged."
```
**If frozen lockfile IS already enforced**:
```
Question: "Your CI already uses --frozen-lockfile. Would you also like to enforce it project-wide via .npmrc?"
Header: "Lock"
Options:
1. Label: "Yes, add to .npmrc (Recommended)"
Description: "Adds frozen-lockfile=true to .npmrc so it's enforced consistently for all developers, not just CI."
2. Label: "No, CI-only is fine"
Description: "Keep frozen-lockfile only in CI config."
```
**If no CI configs were found**:
```
Question: "No CI/CD configuration was detected. Would you like to add frozen-lockfile=true to .npmrc for local enforcement?"
Header: "Lock"
Options:
1. Label: "Yes, add it (Recommended)"
Description: "Adds frozen-lockfile=true to .npmrc. Prevents 'pnpm install' from modifying the lock file."
2. Label: "No, skip"
Description: "Leave lockfile behavior unchanged."
```
**Also check if `frozen-lockfile=true` is already in `.npmrc`**: If it is, skip the question entirely and note: "`.npmrc` already enforces `frozen-lockfile=true`."
Store the user's choice for Phase 6.
### Phase 6: Preview and Apply
Display a clear preview of all changes before writing:
```
Supply Chain Security Configuration Preview
============================================
Package Manager: pnpm [version]
Config File: .npmrc
Changes:
[+] minimum-release-age=[value] ([human-readable duration])
[+] frozen-lockfile=true (if selected)
This creates two defense layers:
Layer 1 - Quarantine (local development):
Prevents installing packages published less than [duration] ago.
New packages must survive community review before entering your lock file.
Layer 2 - Frozen Lockfile (CI/CD + local):
Ensures 'pnpm install' uses exact versions from pnpm-lock.yaml.
Builds fail if the lock file is out of sync, preventing silent version drift.
Learn more: https://charlesjones.dev/blog/npm-supply-chain-attacks-ci-cd-locked-dependencies
```
After displaying the preview, apply the changes:
**If `.npmrc` exists**:
- Use the **Read tool** to get the current contents
- If `minimum-release-age` already exists, use the **Edit tool** to replace the existing value
- If `minimum-release-age` does not exist, use the **Edit tool** to append the new settings
- If `frozen-lockfile` is being added and doesn't already exist, append it as well
- Preserve all existing settings
**If `.npmrc` does not exist**:
- Use the **Write tool** to create `.npmrc` with the new settings
**Formatting rules for .npmrc**:
- Add a blank line before the supply chain settings block if other settings exist above
- Add a comment header: `# Supply chain security`
- Add `minimum-release-age=[value]` on the next line
- If frozen-lockfile was selected, add `frozen-lockfile=true` on the next line
- No trailing blank lines
**Example new .npmrc**:
```ini
# Supply chain security
minimum-release-age=4320
frozen-lockfile=true
```
**Example appended to existing .npmrc**:
```ini
shamefully-hoist=true
# Supply chain security
minimum-release-age=4320
frozen-lockfile=true
```
### Phase 7: Verify and Report
After writing the configuration:
1. **Read** `.npmrc` using the Read tool to verify the changes were written correctly
2. Verify `minimum-release-age` is present with the correct value
3. Verify `frozen-lockfile` is present if it was selected
Display a success message:
```
Supply chain security configured!
.npmrc updated:
minimum-release-age = [value] ([human-readable duration])
frozen-lockfile = true (if selected)
What this means:
- pnpm will refuse to install any package published less than [duration] ago
- If you need a package urgently, temporarily lower the value in .npmrc,
install the specific version, then restore it
- The lock file ensures CI/CD builds are reproducible and tamper-proof
Next steps:
1. Commit .npmrc to version control
2. Run /security-audit for a comprehensive security analysis
3. Run /security-scan-dependencies to check deployed sites for vulnerable libraries
```
### Important Constraints
**DO NOT:**
- Read the contents of any sensitive files during scanning
- Proceed without user confirmation at each decision point
- Use bash test commands (`test -f`, `[ -f ]`, etc.) for file detection
- Modify any files other than `.npmrc`
- Run `pnpm install` after making changes (leave that to the user)
- Skip the package manager detection phase
**DO:**
- Use **Glob tool** for file detection (lock files, CI configs)
- Use **Read tool** to check if `.npmrc` exists
- Use **Write tool** or **Edit tool** to create/update `.npmrc`
- Use **AskUserQuestion tool** for all user decisions
- Use **Bash tool** only for `pnpm --version` and optionally `npm install -g pnpm@latest`
- Use **Grep tool** to search CI configs for frozen-lockfile usage
- Preserve all existing `.npmrc` settings when appending
- Display clear before/after previews
- Convert minutes to human-readable durations in all user-facing output
- Provide context and education at each step so users understand the "why"
### Duration Conversion Reference
Use these conversions in all user-facing output:
| Minutes | Human-Readable |
|---------|----------------|
| 1440 | 24 hours |
| 4320 | 3 days |
| 10080 | 7 days |
| Custom | Calculate: value / 1440 days, or value / 60 hours |
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!