
Dependency Upgrade
- 300 installs
- 202 repo stars
- Updated August 4, 2026
- secondsky/claude-skills
dependency-upgrade is an agent skill that guides secure npm, Bun, pnpm, Yarn, and Deno dependency upgrades with cooldown periods, post-install script blocking, lockfile validation, and staged testing for developers preve
About
dependency-upgrade is a secondsky/claude-skills security skill for managing package upgrades across npm, Bun, pnpm, Yarn, and Deno with supply-chain guardrails. Default mode applies a 7-day min-release-age cooldown, blocks post-install scripts, enforces frozen lockfile installs in CI, and can wire Dependabot or Renovate with matching cooldown rules. Interactive mode walks through cooldown tiers (3–21 days), script allowlists, Socket CLI or npq pre-install auditing, and automerge policy. It ships nine security templates plus eight reference guides covering compatibility matrices, staged upgrades, and publishing hardening. Use it before major framework bumps, after CVE alerts, or when hardening install pipelines against typosquatting and Shai-Hulud-style script attacks.
- dependency-upgrade
Dependency Upgrade by the numbers
- 300 all-time installs (skills.sh)
- +12 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #1,322 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/secondsky/claude-skills --skill dependency-upgradeAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 300 |
|---|---|
| repo stars | ★ 202 |
| Last updated | August 4, 2026 |
| Repository | secondsky/claude-skills ↗ |
How do you upgrade npm packages safely?
Use dependency-upgrade for development tasks
Who is it for?
Backend and platform engineers responsible for dependency hygiene, CI security gates, and incremental major-version migrations across monorepos.
Skip if: Teams that only need a one-line package bump without supply-chain policy, testing gates, or lockfile validation.
When should I use this skill?
User mentions dependency upgrades, Renovate, Dependabot, npm audit, supply-chain attacks, cooldown periods, or blocking postinstall scripts.
What you get
.npmrc or bunfig.toml security configs, Dependabot or renovate.json files, lockfile-lint CI scripts, and tested upgrade branches per package.
- hardened package manager config
- Dependabot or Renovate YAML
- lockfile-lint CI script
By the numbers
- Supports 5 package managers: npm, Bun, pnpm, Yarn, and Deno
- Ships 9 security config templates and 8 reference guides
- Default recommended cooldown is 7 days before installing new releases
Files
Dependency Upgrade
Manage dependency upgrades with supply chain security, compatibility analysis, staged rollout, and comprehensive testing across all major package managers.
When to Use This Skill
- Upgrading major framework or library versions
- Configuring supply chain attack prevention (cooldown, script blocking, lockfile hardening)
- Setting up secure package manager configuration
- Resolving dependency conflicts or peer dependency issues
- Planning incremental upgrade paths with testing
- Automating dependency updates with Renovate, Dependabot, or Snyk
- Auditing dependencies for vulnerabilities
- Setting up CI/CD dependency security workflows
Two Modes of Operation
Interactive — Walk through setup questions to generate tailored config. Use for fresh setup.
Default — Apply recommended defaults immediately: 7-day cooldown, block all scripts, frozen-lockfile, lockfile-lint, Dependabot with cooldown. Customization optional.
Interactive Setup Flow
When the user wants tailored configuration, walk through these decisions. Skip this section entirely if using default mode.
Tier 1: Required Decisions
Always ask these 3 questions before generating any config:
1. Package Manager
"Which package manager does this project use?"
| Answer | Generates |
|---|---|
| npm | .npmrc |
| Bun | bunfig.toml |
| pnpm | pnpm-workspace.yaml |
| Yarn | .yarnrc.yml |
| Deno | deno.json config |
2. Cooldown Period
"How many days should newly published packages age before install? This prevents supply chain attacks where malicious packages are discovered and unpublished within days."
| Option | Days | Use Case |
|---|---|---|
| Aggressive | 3 | Catches most typosquatting |
| Recommended | 7 | Good balance for most projects |
| Conservative | 14 | Critical/production systems |
| Paranoid | 21 | Matches Snyk's built-in default |
| Custom | N | User specifies |
3. Post-Install Script Policy
"How should lifecycle scripts (postinstall, preinstall) be handled? These are the #1 attack vector for supply chain attacks."
| Option | Behavior |
|---|---|
| Block all (recommended) | --ignore-scripts + allow-git=none |
| Allowlist | Block by default, allow specific trusted packages |
| Review only | Warn but don't block |
Tier 2: Security Tooling (Offer as Batch)
"Which of these security features would you like to configure? Select any that apply."
4. CI/CD Automation Tool
| Answer | Generates |
|---|---|
| Dependabot | .github/dependabot.yml with cooldown |
| Renovate | renovate.json with minimumReleaseAge |
| Snyk | No config needed (21-day cooldown built-in) |
| None | Skip |
5. Automerge Policy
| Option | Behavior |
|---|---|
| None | All updates require manual review |
| Minor+Patch only | Auto-merge safe updates, review majors |
| All with approval | Auto-merge after team approval |
6. Update Schedule
| Option | Config Value |
|---|---|
| Daily | "daily" |
| Weekly (default) | "weekly" |
| Biweekly | "biweekly" |
| Monthly | "monthly" |
7. Install-Time Security Tooling
"Which security tools should protect dependency installation?"
| Option | Free? | What It Does |
|---|---|---|
| socket npm wrapper | Yes (beta) | Wraps npm/npx, blocks malicious packages before install. Run socket wrapper on to enable system-wide. |
| npq | Yes | Pre-install auditor (CVE, typosquat, age, provenance checks) |
| Socket Firewall (sfw) | No | Real-time deep analysis, blocks malicious packages |
| socket npm + npq | Yes | Both free tools combined |
| None | — | Skip |
Load references/socket-cli-guide.md for full Socket CLI setup including authentication and free vs authenticated features.
8. Lockfile Validation
| Option | Behavior |
|---|---|
| Yes (recommended) | Adds lockfile-lint + CI script |
| No | Skip |
Tier 3: Advanced Options (Only If User Opts In)
"Would you like to configure any advanced options?"
9. Dev Containers — Generate hardened .devcontainer/devcontainer.json (Yes/No)
10. Secrets Manager — 1Password CLI / Infisical / None
11. pnpm Trust Policy — Enable trustPolicy: no-downgrade (pnpm 10.21+ only, Yes/No)
12. Cooldown Exclusions — Package names that bypass cooldown (e.g., @types/react, typescript, esbuild)
Security-First Upgrade Principles
1. Cooldown before installing — Wait 7 days for new package versions to be vetted by the community 2. Block post-install scripts — Prevent arbitrary code execution during npm install 3. Freeze lockfiles in CI — Use deterministic installs (npm ci, --frozen-lockfile) 4. Validate lockfile integrity — Use lockfile-lint to detect injection 5. Audit before trusting — Use npq or Socket CLI to check packages before installing 6. Upgrade incrementally — One major version at a time with testing between each 7. Never blindly upgrade — Avoid npm update or npm-check-updates -u without review 8. Scan before and after — Use socket scan to detect supply chain issues beyond CVEs
Cooldown Period: Prevent Supply Chain Attacks
Newly published packages may contain malicious code discovered within hours. Configure a cooldown period to delay installation.
Quick Setup
npm (.npmrc):
min-release-age=7Bun (bunfig.toml):
[install]
minimumReleaseAge = 604800 # 7 days in seconds
minimumReleaseAgeExcludes = ["@types/bun", "typescript"]pnpm (pnpm-workspace.yaml):
minimumReleaseAge: 10080 # 7 days in minutes
minimumReleaseAgeExclude:
- '@types/react'
- typescriptYarn (.yarnrc.yml):
npmMinimalAgeGate: "7d"
npmPreapprovedPackages:
- "@types/react"
- "typescript"Load references/cooldown-config-guide.md for detailed per-PM configuration, CI tool integration, and exclusion patterns.
Use templates/<pm>-security.tmpl for copy-paste ready config files.
Disable Post-Install Scripts
Post-install scripts are the most common supply chain attack vector (Shai-Hulud, Nx, event-stream incidents).
Quick Setup
npm:
npm config set ignore-scripts true
npm config set allow-git noneBun: Disabled by default. Allow specific packages in package.json:
{ "trustedDependencies": ["esbuild", "sharp"] }pnpm (10.0+): Disabled by default. Allow specific packages in pnpm-workspace.yaml:
allowBuilds:
esbuild: true
strictDepBuilds: true # Hard error on unreviewed scriptsLoad references/package-manager-security.md for full per-PM hardening including pnpm trustPolicy, blockExoticSubdeps, and @lavamoat/allow-scripts.
Deterministic & Frozen Installs
Always use frozen install commands in CI to ensure reproducible builds:
| Package Manager | Command | What It Does |
|---|---|---|
| npm | npm ci | Deletes node_modules, installs exact lockfile versions |
| Bun | bun install --frozen-lockfile | Fails if lockfile is out of sync |
| pnpm | pnpm install --frozen-lockfile | Fails if lockfile is out of sync |
| Yarn | yarn install --immutable --immutable-cache | Validates lockfile and cache |
| Deno | deno install --frozen | Frozen installation |
Commit all lockfiles to version control: package-lock.json, bun.lock, pnpm-lock.yaml, yarn.lock, deno.lock.
Lockfile Validation
Install and configure lockfile-lint to detect lockfile injection attacks:
npm install --save-dev lockfile-lint{
"scripts": {
"lint:lockfile": "lockfile-lint --path package-lock.json --type npm --allowed-hosts npm --validate-https",
"preinstall": "npm run lint:lockfile"
}
}Note: lockfile-lint does not currently support Bun's bun.lock / bun.lockb formats.
Pre-Install Security Auditing
npq — Pre-Install Auditor
npm install -g npq
npq install <package> # Audit before installing
npq install <package> --dry-run # Audit without installing
# Shell alias for seamless use
alias npm='npq-hero'
# Use with other PMs
NPQ_PKG_MGR=pnpm npq install <package>
NPQ_PKG_MGR=bun npq install <package>Socket Firewall (sfw) — Real-Time Blocker
npm install -g sfw
sfw npm install <package> # Blocks malicious packages
sfw pnpm add <package>
sfw yarn add <package>Load references/supply-chain-security.md for full comparison of npq vs sfw and what each validates.
Socket CLI Integration
Socket CLI provides proactive supply chain security beyond basic vulnerability scanning — covering malware detection, typosquatting, protestware, install script risks, and license compliance.
Proactive Upgrade Workflow
1. PRE-UPGRADE: socket scan create --report → establish baseline
2. EVALUATE: socket package score npm <pkg>@<ver> → assess target package safety
3. SAFE INSTALL: socket npm install <pkg> → block malicious packages
4. POST-UPGRADE: socket scan create --report → verify no new alerts
5. DIFF: socket scan diff <before> <after> → see exactly what changed
6. FIX: socket fix --minimum-release-age 7d → auto-fix any new CVEs
7. OPTIMIZE: socket optimize → apply security overridesQuick Reference
# Install
npm install -g socket
# Authenticate (required for scans, fixes, package scores)
socket login
# Check a package before upgrading
socket package score npm <package>
# Scan your whole project
socket scan create --report
# Auto-fix CVEs (complements Dependabot/Renovate)
socket fix --minimum-release-age 7d
# Gate CI on security policy
socket ci
# Safe npm wrapper (free, no auth needed)
socket wrapper onLoad references/socket-cli-guide.md for comprehensive command reference, CI workflow templates, alert categories, and free vs authenticated feature matrix.
Dependency Analysis
# Audit for vulnerabilities
bun audit # Bun
npm audit # npm
yarn audit # Yarn
# Socket: deep security assessment (CVEs + supply chain + license)
socket package score npm <package>
socket scan create --report
# Check for outdated packages
bun outdated
npm outdated
# Interactive upgrade (safe — review each)
bunx npm-check-updates --interactive
# Analyze dependency tree
npm ls <package-name>
yarn why <package-name>Staged Upgrade Strategy
Upgrade one dependency at a time with testing between each:
# 1. Create feature branch
git checkout -b upgrade/<package>-<version>
# 2. (Optional) Baseline scan — capture current state
socket scan create --report
# 3. Evaluate target package before upgrading
socket package score npm <package>@<version>
# 4. Upgrade single package
bun add <package>@<version>
# 5. Test immediately
bun test && bunx tsc --noEmit && bun run build
# 6. (Optional) Post-upgrade scan — verify no new alerts
socket scan create --report
# 7. Commit and continue
git add -A && git commit -m "chore: upgrade <package> to <version>"Load references/staged-upgrades.md for codemod automation, custom migration scripts, and peer dependency handling.
Load references/compatibility-matrix.md for version compatibility tables (React 18/19, Next.js 13-15, TypeScript, Tailwind 3/4).
Automated Updates with Cooldown
Configure CI/CD tools to respect cooldown periods:
Dependabot (.github/dependabot.yml)
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
cooldown:
default-days: 7Renovate (renovate.json)
{
"extends": ["config:base"],
"minimumReleaseAge": "7 days",
"packageRules": [
{
"matchUpdateTypes": ["minor", "patch"],
"automerge": true
},
{
"matchUpdateTypes": ["major"],
"automerge": false,
"minimumReleaseAge": "14 days"
}
]
}Snyk
Snyk includes a built-in 21-day cooldown for upgrade PRs. No configuration needed.
Socket Fix (complements Dependabot/Renovate)
Socket Fix automatically resolves CVEs with intelligent upgrade planning. Runs alongside other automation tools — it focuses on CVE remediation specifically:
# Fix all fixable CVEs with cooldown alignment
socket fix --minimum-release-age 7d
# Conservative: no major version bumps
socket fix --minimum-release-age 7d --no-major-updates
# Target specific CVEs
socket fix --id GHSA-hhq3-ff78-jv3g --minimum-release-age 7d
# Preview without applying
socket fix --no-apply-fixes --minimum-release-age 7dFor CI autopilot mode (auto-creates and auto-merges fix PRs), use templates/socket-fix-ci.tmpl.
Load references/socket-cli-guide.md for full socket fix options including --autopilot, --range-style, and --pr-limit.
Use templates/dependabot-security.tmpl or templates/renovate-security.tmpl for complete config files.
Publishing Security
For package maintainers:
# Enable 2FA
npm profile enable-2fa auth-and-writes
# Publish with provenance (cryptographic build proof)
npm publish --provenance
# Trusted publishing via OIDC (eliminates long-lived tokens)
# Configure on npmjs.com, then:
# In GitHub Actions: permissions: id-token: writeLoad references/supply-chain-security.md for full publishing security guide including OIDC setup and dependency tree reduction.
Dev Environment Hardening
Isolate dependency execution from the host system:
- Dev containers — limit blast radius of malicious packages
- Secrets management — use 1Password CLI or Infisical instead of plaintext
.envfiles - Dependency tree reduction — replace common packages with native JS
Use templates/devcontainer-security.tmpl for a hardened dev container config.
Load references/secrets-and-containers.md for dev container setup, secrets management, and dependency reduction patterns.
Testing Strategy
Run tests at every level after each upgrade:
# 1. Static analysis (fastest)
bunx tsc --noEmit && bun run lint
# 2. Unit tests
bun test
# 3. Build check
bun run build
# 4. Integration / E2E (after major upgrades)
bun run test:e2eLoad references/testing-strategy.md for full testing pyramid, CI integration, and bundle analysis.
Rollback Plan
#!/bin/bash
git stash
git checkout -b upgrade/<package>
bun add <package>@latest
if bun test && bun run build; then
git add package.json bun.lock
git commit -m "chore: upgrade <package>"
else
echo "Upgrade failed, rolling back"
git checkout main
git branch -D upgrade/<package>
bun install
fiUpgrade Checklist
Pre-Upgrade:
- [ ] Review current dependency versions
- [ ] Read changelogs for breaking changes
- [ ] Create feature branch
- [ ] Tag current state (git tag pre-upgrade)
- [ ] Run full test suite (baseline)
- [ ] Verify cooldown period is configured
Security Pre-Checks:
- [ ] Post-install scripts are disabled
- [ ] Lockfile validation is active
- [ ] Install auditing tools configured (if applicable)
- [ ] CI uses frozen-lockfile install
- [ ] Run `socket scan create --report` for baseline (if Socket available)
During Upgrade:
- [ ] Upgrade one dependency at a time
- [ ] Check target package: `socket package score npm <pkg>` (if Socket available)
- [ ] Respect cooldown period (don't force latest)
- [ ] Update peer dependencies
- [ ] Fix TypeScript errors
- [ ] Run test suite after each upgrade
- [ ] Check bundle size impact
Post-Upgrade:
- [ ] Post-upgrade scan: `socket scan diff` to verify no new alerts (if Socket available)
- [ ] Consider `socket fix --minimum-release-age 7d` for any new CVEs
- [ ] Full regression testing
- [ ] Performance testing
- [ ] Update documentation
- [ ] Deploy to staging
- [ ] Monitor for errors
- [ ] Deploy to productionCommon Pitfalls
- Upgrading all dependencies at once (use incremental upgrades)
- Blindly running
npm updateornpm-check-updates -uwithout review - Not testing after each individual upgrade
- Ignoring peer dependency warnings
- Forgetting to update or commit the lock file
- Not reading breaking change notes in changelogs
- Skipping major versions instead of stepping through them
- Not having a rollback plan
- Trusting npmjs.org displayed source code (can differ from actual tarball)
- Leaving post-install scripts enabled (most common attack vector)
- Not configuring a cooldown period for new package versions
When to Load References
Load these reference files when the user needs detailed information beyond the quick-reference in SKILL.md:
| Load This File | When |
|---|---|
references/cooldown-config-guide.md | Configuring cooldown for a specific PM, CI tool integration, or exclusion patterns |
references/package-manager-security.md | Full per-PM hardening guide including pnpm trust policy, blockExoticSubdeps, cross-PM cheat sheet |
references/supply-chain-security.md | Understanding attack vectors, incident history, npq vs sfw vs Socket CLI comparison, publisher security (2FA, provenance, OIDC) |
references/secrets-and-containers.md | Setting up dev containers, secrets management with 1Password/Infisical |
references/socket-cli-guide.md | Using Socket CLI for scans, fixes, package scoring, CI integration, wrapper mode, alert categories |
references/compatibility-matrix.md | Checking version compatibility for React, Next.js, TypeScript, Tailwind upgrades |
references/staged-upgrades.md | Codemod automation, custom migration scripts, peer dependency handling, workspace upgrades |
references/testing-strategy.md | Full testing pyramid, CI integration, bundle analysis, performance testing |
Template Files
Ready-to-use config files in templates/:
| Template | Purpose |
|---|---|
npmrc-security.tmpl | Secure .npmrc with scripts disabled + cooldown |
bunfig-security.tmpl | Secure bunfig.toml with cooldown + exclusions |
pnpm-workspace-security.tmpl | Secure pnpm-workspace.yaml with cooldown, allowBuilds, trustPolicy |
yarnrc-security.tmpl | Secure .yarnrc.yml with age gate + preapproved packages |
dependabot-security.tmpl | Dependabot config with 7-day cooldown |
renovate-security.tmpl | Renovate config with minimumReleaseAge + automerge rules |
devcontainer-security.tmpl | Hardened dev container with security options |
socket-fix-ci.tmpl | GitHub Actions: Socket Fix autopilot with cooldown-aligned CVE remediation |
socket-scan-ci.tmpl | GitHub Actions: Socket CI security gate for every push/PR |
Dependency Compatibility Matrix
Common version compatibility requirements for major frameworks and libraries.
React Ecosystem
| React | react-dom | react-router | @testing-library/react | Next.js |
|---|---|---|---|---|
| 18.x | ^18.0.0 | ^6.0.0 | ^14.0.0 | 13.x-14.x |
| 19.x | ^19.0.0 | ^7.0.0 | ^16.0.0 | 15.x |
React 18 → 19 Migration
bun add react@19 react-dom@19
bun add -D @testing-library/react@16 @types/react@19 @types/react-dom@19Key breaking changes:
- Ref as prop (no more
forwardRefneeded) useDeferredValueinitial value support- Improved hydration mismatch reporting
refcleanup functions return value is ignored
React Router 6 → 7
bun add react-router@7 react-router-dom@7React Router v7 is the successor to Remix and React Router v6. Key changes:
- Framework mode (Remix-like) vs library mode
- New
createRootRoute,createRouterAPI - Data loading via
loaderandaction
Next.js
| Next.js | React | Node.js | TypeScript |
|---|---|---|---|
| 13.x | 18.x | >=16.8 | >=4.5 |
| 14.x | 18.x | >=18.17 | >=4.5 |
| 15.x | 18.x-19.x | >=20.9 | >=4.5 |
Next.js 14 → 15 Migration
bun add next@15Key breaking changes:
- Turbopack as default dev bundler
- Improved caching defaults (fetch requests no longer cached by default)
next/imagechanges- Node.js >=20.9 required
TypeScript
| TypeScript | Node.js target | Key feature |
|---|---|---|
| 5.3 | ES2022+ | using keyword, Symbol.dispose |
| 5.4 | ES2022+ | NoInfer utility type |
| 5.5 | ES2022+ | Inferred type predicates |
| 5.6 | ES2022+ | Iterator helpers |
| 5.7 | ES2024+ | --target es2024, path renaming |
TypeScript Upgrade
bun add -D typescript@latestTypeScript upgrades are typically backward compatible. Run type-check after upgrade:
bunx tsc --noEmitTailwind CSS
| Tailwind | PostCSS | Framework support |
|---|---|---|
| 3.x | >=8.4 | All frameworks |
| 4.x | Built-in | All frameworks (new engine) |
Tailwind 3 → 4 Migration
bun add -D tailwindcss@4 @tailwindcss/viteKey changes:
- CSS-first configuration (
@themeinstead oftailwind.config.js) - Required primary entrypoint:
@import "tailwindcss" - PostCSS plugin via
@tailwindcss/postcsspackage (must install and configure) - Lightning CSS can replace PostCSS in some setups per official Tailwind recommendations
- New
@source inline("class-name")pattern for safelisting utilities
Node.js Version Compatibility
| Node.js | npm version | npm ci support |
|---|---|---|
| 18 LTS | 9.x | Yes |
| 20 LTS | 10.x | Yes |
| 22 LTS | 10.x | Yes |
See Node.js Release Schedule for LTS dates.
Checking Compatibility
# Check peer dependency warnings
npm ls 2>&1 | grep -i "peer dep"
# Check for outdated with compatibility info
npm outdated
# Use npm-check-updates for safe upgrade analysis
bunx npm-check-updates --interactive
# Check for known vulnerabilities
npm audit
bun auditCooldown Period Configuration Guide
Detailed configuration for delaying installation of newly published packages across all major package managers and CI/CD tools.
Why a Cooldown Period?
Newly released packages may contain malicious code that the community discovers within hours or days. A cooldown period prevents installing versions before they've been vetted.
Recommended minimum: 7 days — balances security with developer productivity.
| Period | Risk Level | Use Case |
|---|---|---|
| 3 days | Aggressive | Catches most typosquatting; may miss delayed discoveries |
| 7 days | Recommended | Good balance; catches most supply chain incidents |
| 14 days | Conservative | Critical/production systems |
| 21 days | Paranoid | Matches Snyk's built-in default |
Package Manager Configuration
npm
# .npmrc — project-level
min-release-age=7# Global config (all projects on machine)
npm config set min-release-age 7
# Per-command with dynamic date
npm install express --before="$(date -v -7d)"
# Per-command with specific date
npm install express --before=2025-01-01Notes:
min-release-ageis persistent and works with all install commands--beforerequires manual date management; prefermin-release-agefor automation
Bun (1.3+)
# bunfig.toml
[install]
# Only install versions published at least 7 days ago
minimumReleaseAge = 604800 # seconds (7 days)
# Packages that bypass the cooldown
minimumReleaseAgeExcludes = ["@types/bun", "typescript"]The minimumReleaseAge value is in seconds:
- 3 days = 259200
- 7 days = 604800
- 14 days = 1209600
- 21 days = 1814400
pnpm (10.16+)
# pnpm-workspace.yaml
minimumReleaseAge: 10080 # 7 days (in minutes)
# Packages that bypass the cooldown
minimumReleaseAgeExclude:
- '@types/react'
- typescriptThe minimumReleaseAge value is in minutes:
- 3 days = 4320
- 7 days = 10080
- 14 days = 20160
- 21 days = 30240
Yarn (4.10+)
# .yarnrc.yml
# Only consider versions published at least 7 days ago
npmMinimalAgeGate: "7d"
# Packages that bypass the age gate (descriptors or glob patterns)
npmPreapprovedPackages:
- "@types/react"
- "typescript"The npmMinimalAgeGate accepts human-readable durations: "3d", "7d", "14d", "21d".
CI/CD Tool Configuration
Dependabot
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
# Wait 7 days after a new version is published before creating a PR
cooldown:
default-days: 7
open-pull-requests-limit: 5Renovate Bot
{
"extends": ["config:base"],
"minimumReleaseAge": "7 days",
"packageRules": [
{
"matchUpdateTypes": ["minor", "patch"],
"automerge": true,
"minimumReleaseAge": "7 days"
},
{
"matchUpdateTypes": ["major"],
"automerge": false,
"minimumReleaseAge": "14 days",
"labels": ["major-update"]
}
]
}Snyk
Snyk includes a built-in 21-day cooldown for automated dependency upgrade PRs. No configuration needed — it automatically avoids recommending versions less than 21 days old to prevent:
- Versions with functional bugs that get unpublished
- Versions from compromised accounts
To adjust, use Snyk's upgrade PR settings in the dashboard.
Cooldown Exclusions
Some packages should bypass the cooldown because they are closely tied to the toolchain and need rapid updates:
Common Exclusions
| Package | Reason |
|---|---|
@types/react | Type definitions must match React version exactly |
@types/node | Type definitions must match Node version |
typescript | Build tooling; often needs same-day patches |
@types/bun | Bun type definitions must match runtime version |
esbuild | Build tool; security patches need fast rollout |
@cloudflare/workers-types | Platform types must match runtime |
Configuring Exclusions
npm: No built-in exclusion mechanism. Use --before flag manually for excluded packages.
Bun:
minimumReleaseAgeExcludes = ["@types/bun", "typescript", "esbuild"]pnpm:
minimumReleaseAgeExclude:
- '@types/react'
- typescript
- esbuildYarn:
npmPreapprovedPackages:
- "@types/react"
- "typescript"
- "esbuild"Multi-PM Projects
For projects that support multiple package managers, ensure cooldown is configured in all relevant files:
| PM | Config File | Setting |
|---|---|---|
| npm | .npmrc | min-release-age=7 |
| Bun | bunfig.toml | minimumReleaseAge = 604800 |
| pnpm | pnpm-workspace.yaml | minimumReleaseAge: 10080 |
| Yarn | .yarnrc.yml | npmMinimalAgeGate: "7d" |
| Dependabot | .github/dependabot.yml | cooldown.default-days: 7 |
| Renovate | renovate.json | "minimumReleaseAge": "7 days" |
Verifying Cooldown Is Active
# npm — check config
npm config get min-release-age
# Bun — verify bunfig.toml is loaded
cat bunfig.toml | grep minimumReleaseAge
# pnpm — verify workspace config
cat pnpm-workspace.yaml | grep minimumReleaseAge
# Yarn — verify config
cat .yarnrc.yml | grep npmMinimalAgeGatePackage Manager Security Hardening
Per-package-manager security configuration covering post-install scripts, deterministic installs, and supply chain protections.
npm
Disable Lifecycle Scripts
# .npmrc
ignore-scripts=true
allow-git=none# Global
npm config set ignore-scripts true
npm config set allow-git none--allow-git=none (npm CLI 11.10.0+) prevents git-based dependencies from shipping .npmrc files that re-enable lifecycle scripts, closing a bypass vector.
Deterministic Installs
# CI/CD — strict lockfile adherence
npm ci
npm ci --only=production
# Never use npm install in CInpm ci:
- Deletes
node_modulesbefore installing - Requires
package-lock.jsonto exist - Fails if lockfile is out of sync with
package.json - Installs exact versions from lockfile only
Lockfile Validation
npm install --save-dev lockfile-lint
npx lockfile-lint \
--path package-lock.json \
--type npm \
--allowed-hosts npm yarn \
--validate-httpsCooldown
# .npmrc
min-release-age=7Bun
Post-Install Script Control
Bun disables postinstall scripts by default. Allow specific packages via package.json:
{
"trustedDependencies": [
"esbuild",
"sharp"
]
}Deterministic Installs
# Frozen lockfile mode (CI)
bun install --frozen-lockfileCooldown
# bunfig.toml
[install]
minimumReleaseAge = 604800 # 7 days in seconds
minimumReleaseAgeExcludes = ["@types/bun", "typescript"]Lockfile Notes
Bun uses bun.lock (text, default since v1.2) or bun.lockb (binary). lockfile-lint does not support Bun lockfile formats currently.
pnpm
Post-Install Script Control (10.0+)
pnpm disables postinstall scripts by default since v10.0.
# pnpm-workspace.yaml
# Preferred (pnpm 10.26+)
allowBuilds:
esbuild: true
fsevents: true
nx@21.6.4 || 21.6.5: true
core-js: false
# Legacy (still supported)
# onlyBuiltDependencies:
# - esbuild
# - fsevents
# Hard error on unreviewed scripts (pnpm 10.3+)
strictDepBuilds: trueTrust Policy (pnpm 10.21+)
Detect when a package's trust level has decreased — early signal of account compromise:
# pnpm-workspace.yaml
trustPolicy: no-downgrade
trustPolicyExclude:
- 'chokidar@4.0.3'
- 'webpack@4.47.0 || 5.102.1'
# Ignore packages published >30 days ago (pnpm 10.27+)
trustPolicyIgnoreAfter: 43200 # minutesTrust levels (strongest → weakest): 1. Trusted Publisher (OIDC/GitHub Actions) 2. Provenance (npm provenance attestation) 3. Signatures (registry signature) 4. No evidence
Block Exotic Transitive Dependencies (pnpm 10.26+)
# pnpm-workspace.yaml
blockExoticSubdeps: truePrevents transitive dependencies from using git repos or direct tarball URLs. Only direct dependencies in package.json may use exotic sources.
Deterministic Installs
# Frozen lockfile (CI)
pnpm install --frozen-lockfileLockfile Security
pnpm is inherently more resistant to lockfile injection:
- Doesn't maintain modifiable tarball sources
- Won't install lockfile packages not declared in
package.json pnpm-lock.yamlformat is more resistant to injection
Cooldown
# pnpm-workspace.yaml
minimumReleaseAge: 10080 # 7 days in minutes
minimumReleaseAgeExclude:
- '@types/react'
- typescriptYarn
Deterministic Installs
# Validate lockfile did not mutate
yarn install --immutable --immutable-cacheCooldown (Yarn 4.10+)
# .yarnrc.yml
npmMinimalAgeGate: "7d"
npmPreapprovedPackages:
- "@types/react"
- "typescript"Lockfile Validation
npx lockfile-lint \
--path yarn.lock \
--type yarn \
--allowed-hosts npm yarn \
--validate-httpsDeno
Deterministic Installs
deno install --frozenLockfile
Deno uses deno.lock. Ensure it's committed to version control.
Cross-PM Cheat Sheet
| Feature | npm | Bun | pnpm | Yarn | Deno |
|---|---|---|---|---|---|
| Disable scripts | ignore-scripts=true | Default off | Default off (10.0+) | N/A | N/A |
| Script allowlist | @lavamoat/allow-scripts | trustedDependencies | allowBuilds | N/A | N/A |
| Frozen install | npm ci | --frozen-lockfile | --frozen-lockfile | --immutable | --frozen |
| Cooldown | min-release-age | minimumReleaseAge (sec) | minimumReleaseAge (min) | npmMinimalAgeGate | N/A |
| Lockfile format | package-lock.json | bun.lock / bun.lockb | pnpm-lock.yaml | yarn.lock | deno.lock |
| Lockfile lint | lockfile-lint | Not supported | lockfile-lint | lockfile-lint | N/A |
| Trust policy | N/A | N/A | trustPolicy (10.21+) | N/A | N/A |
| Block exotic deps | N/A | N/A | blockExoticSubdeps (10.26+) | N/A | N/A |
| Lockfile to commit | package-lock.json | bun.lock | pnpm-lock.yaml | yarn.lock | deno.lock |
Committing Lockfiles
Always commit lockfiles to version control:
git add package-lock.json # npm
git add bun.lock # Bun
git add pnpm-lock.yaml # pnpm
git add yarn.lock # Yarn
git add deno.lock # DenoNever add lockfiles to .gitignore. They are the source of truth for reproducible installs.
Dev Environment Hardening: Secrets & Containers
Isolate dependency execution and protect sensitive data from supply chain attacks.
Dev Containers
Running npm install on a host machine exposes the entire system to malicious packages. Dev containers limit blast radius.
Basic Setup
Create .devcontainer/devcontainer.json:
{
"name": "Node.js Dev Container",
"image": "mcr.microsoft.com/devcontainers/javascript-node:20",
"features": {
"ghcr.io/devcontainers/features/1password:1": {}
},
"postCreateCommand": "npm ci",
"customizations": {
"vscode": {
"extensions": ["dbaeumer.vscode-eslint"]
}
}
}Hardened Setup
{
"name": "Node.js Hardened Dev Container",
"image": "mcr.microsoft.com/devcontainers/javascript-node:20",
"runArgs": [
"--security-opt=no-new-privileges:true",
"--cap-drop=ALL",
"--cap-add=CHOWN",
"--cap-add=SETUID",
"--cap-add=SETGID"
],
"containerEnv": {
"NODE_OPTIONS": "--disable-proto=delete"
},
"postCreateCommand": "npm ci"
}Security controls:
no-new-privileges:true— prevents privilege escalation--cap-drop=ALL— drops all Linux capabilities- Minimal capabilities added back:
CHOWN,SETUID,SETGID(needed for npm) --disable-proto=delete— hardens JavaScript prototype chain
What This Prevents
When a malicious package executes during npm install:
- Without container: Access to all files, SSH keys, env vars, other projects
- With container: Confined to container filesystem, no host access
Secrets Management
The Problem with .env Files
Plaintext secrets in .env files are accessible to any code running in the process:
# DANGEROUS — plaintext secrets
DATABASE_PASSWORD=my-secret-password
API_KEY=sk-1234567890abcdefSupply chain attacks can read process.env or scan for .env files on the filesystem.
1Password CLI Integration
# Install 1Password CLI
brew install 1password-cli
# Use secret references in .env
DATABASE_PASSWORD=op://vault/database/password
API_KEY=op://vault/project/api-key
# Run with secret injection
op run -- npm start
# With explicit env file
op run --env-file="./.env" -- node --env-file="./.env" server.jsSecret references are resolved at runtime with additional authentication (Touch ID on macOS). The actual secret values never exist in files.
Infisical Integration
# Install Infisical CLI
brew install infisical
# Use secret references
DATABASE_PASSWORD=infisical://project/env/api-key
# Run with secret injection
infisical run -- npm startBun-Specific Notes
# Bun supports .env files natively
# Use with secret manager:
op run -- bun run dev
# Bun also supports --env-file flag
op run -- bun --env-file=./.env run devResources
Socket CLI Guide
Comprehensive reference for using Socket CLI to secure dependency upgrades with proactive scanning, automated CVE fixing, and CI enforcement.
Installation & Authentication
npm install -g socketAuthentication
# Interactive login (stores token locally)
socket login
# Or set API token via environment variable
export SOCKET_SECURITY_API_TOKEN=your-token-here
# Or per-command
SOCKET_SECURITY_API_TOKEN=xyz socket scan create --reportGenerate API tokens at: https://socket.dev/settings/api-keys
Required permissions vary per command (see each section below).
Free vs Authenticated Features
| Feature | Free (No Token) | Authenticated |
|---|---|---|
socket npm / socket npx | Yes (beta, default issues only) | Yes |
socket wrapper on/off | Yes | Yes |
socket package shallow | No | Yes (1 unit/pkg) |
socket package score (deep) | No | Yes (1 unit/pkg) |
socket scan create | No | Yes (1 unit) |
socket scan report | No | Yes (2 units) |
socket ci | No | Yes (1 unit + report) |
socket fix | No | Yes (101 units) |
socket optimize | No | Yes |
Package Assessment
Evaluate packages before adding or upgrading them.
Shallow Score (package only)
Quick assessment of a single package excluding its dependencies:
socket package shallow npm express
socket package shallow npm express@4.18.2
# Multiple packages, mixed ecosystems
socket package shallow pkg:npm/express pkg:pypi/requests
# Output formats
socket package shallow npm express --json
socket package shallow npm express --markdownReturns scores for: Supply Chain Risk, Maintenance, Quality, Vulnerabilities, License. Also lists detected alerts with severity levels.
Deep Score (package + all transitives)
Full assessment including all transitive dependencies:
socket package score npm eslint
socket package score npm eslint --markdown
# Specify exact version via purl
socket package score 'pkg:npm/[email protected]'The deep score reflects the minimum score across all transitive dependencies. A package with a high shallow score can have a low deep score if one of its dependencies is risky.
When to Use Each
| Scenario | Use | Why |
|---|---|---|
| Quick check before installing | shallow | Fast, evaluates just the target |
| Evaluating a major upgrade | score (deep) | Catches transitive supply chain risks |
| CI policy gate | scan (not package) | Evaluates your whole project |
| Comparing two packages | shallow | Quick side-by-side comparison |
Project Scanning
Scan your entire project for security issues.
Create a Scan
# Basic scan (auto-detects manifest files)
socket scan create
# Scan with policy report (recommended)
socket scan create --report
# Associate with repo/branch (for dashboard)
socket scan create --repo=my-project --branch=main --default-branch --report
# JSON output for automation
socket scan create --report --json
# Markdown output for sharing
socket scan create --report --markdownA scan uploads manifest files (package.json, requirements.txt, etc.) to Socket for analysis. No source code is sent.
API requirements: 1 unit + full-scans:create permission. Report adds 2 units + full-scans:list + security-policy:read.
CI Gate: socket ci
Shorthand for socket scan create --report. Creates a scan and exits with code 0 if the project passes your org's security policy, non-zero otherwise:
socket ciUse in CI pipelines to block merges that introduce security policy violations.
Scan Reports
# View scan report with alert folding
socket scan report <SCAN_ID> --fold=version --json
# Include license policy
socket scan report <SCAN_ID> --license --markdown
# Quick health check (just true/false)
socket scan report <SCAN_ID> --shortFold levels: none (every occurrence) → file → version (recommended) → pkg.
Scan Diffs
Compare two scans to see exactly what changed between upgrades:
socket scan diff <SCAN_ID_BEFORE> <SCAN_ID_AFTER>
# JSON for automation
socket scan diff <ID1> <ID2> --json > scan-delta.json
# Markdown for PR comments
socket scan diff <ID1> <ID2> --markdownShows packages added, removed, and changed — plus any new or resolved alerts.
API requirements: 1 unit + full-scans:list permission.
Automated CVE Fixing: socket fix
Automatically upgrade vulnerable dependencies to secure versions with intelligent upgrade planning.
Basic Usage
# Fix all fixable vulnerabilities
socket fix
# Fix specific CVEs
socket fix --id GHSA-hhq3-ff78-jv3g
socket fix --id CVE-2021-23337
# Multiple IDs
socket fix --id GHSA-xxxx-xxxx-xxxx,GHSA-yyyy-yyyy-yyyy
socket fix --id GHSA-xxxx --id GHSA-yyyy
# Fix in specific project directory
socket fix ./path/to/projectCooldown-Aligned Fixing
Align with your cooldown policy using --minimum-release-age:
# Only fix with packages vetted for at least 7 days (matches recommended cooldown)
socket fix --minimum-release-age 7d
# Conservative: 14 days
socket fix --minimum-release-age 14d
# Aggressive: 3 days
socket fix --minimum-release-age 3dTime formats: 1h (hours), 3d (days), 2w (weeks).
Conservative Options
# Don't suggest major version upgrades (less risk of breakage)
socket fix --no-major-updates
# Preview changes without applying them
socket fix --no-apply-fixes
# Output suggested fixes to file
socket fix --no-apply-fixes --output-file suggested-fixes.json
# Show which direct deps introduce transitive CVEs
socket fix --show-affected-direct-dependencies --output-file fixes.json
# Pin to exact versions instead of preserving ranges
socket fix --range-style pinCI/PR Mode (Autopilot)
Run in GitHub Actions to automatically create fix PRs:
# Create PRs for fixable CVEs (auto-merge if checks pass)
socket fix --autopilot
# Limit number of PRs per run
socket fix --autopilot --pr-limit 5Required environment variables for CI:
SOCKET_CLI_GITHUB_TOKEN(orGITHUB_TOKEN) — for PR creationSOCKET_CLI_GIT_USER_NAME— git commit author nameSOCKET_CLI_GIT_USER_EMAIL— git commit author emailSOCKET_CLI_API_TOKEN— Socket API token
API requirements: 101 units + full-scans:create + packages:list permissions.
Output Formats
socket fix --json
socket fix --markdown > security-fixes.mdDependency Optimization: socket optimize
Apply @socketregistry overrides to patch known issues without changing direct dependency versions:
# Apply overrides
socket optimize
# Pin overrides to exact versions
socket optimize --pin
# Production dependencies only
socket optimize --prod
# For a specific project
socket optimize ./path/to/projectThis adds overrides (npm/pnpm) or resolutions (yarn) to your package.json that redirect vulnerable transitive dependencies to Socket's secure patches.
Safe Install Wrappers
socket npm and socket npx
Run npm/npx through Socket to check packages before installation:
# Install with Socket protection
socket npm install express
socket npm install -g typescript
# Run commands safely
socket npx create-react-app my-appThese wrappers intercept the actual npm/npx resolution, check all resolved packages against Socket's database, and prompt before installing flagged packages.
Beta limitations: Uses default issue set only (not configurable without auth). Windows limited to WSL.
System-Wide Wrapper: socket wrapper
Enable automatic interception of all npm/npx commands on your system:
# Enable (creates shell aliases)
socket wrapper on
# Disable
socket wrapper offAfter enabling, any npm install ... command automatically runs through Socket. Requires restarting your shell or sourcing your RC file (e.g., source ~/.zshrc).
Manual Shell Aliases
# Add to ~/.zshrc or ~/.bashrc
alias npm="socket-npm"
alias npx="socket-npx"
# zsh autocompletions
compdef _npm socket-npm
# bash autocompletions
$(complete -p npm | sed 's/npm$/socket-npm/')Combining with npq
npq (pre-install auditor) can use socket-npm as its package manager:
NPQ_PKG_MGR=socket-npm npq install expressNote: socket npm performs full transitive analysis and integrates into npm's install flow, so it's more thorough than npq's checks. Running both may be redundant.
Project Configuration: socket.json
Store per-project defaults to avoid repeating flags:
# Interactive setup
socket scan setupCreates socket.json in the project root:
{
"repo": "my-project",
"branch": "main",
"defaultBranch": true
}After setup, socket scan create automatically uses these values. Flags still override config.
Commit socket.json to share defaults with your team, or add to .gitignore for personal use.
Alert Categories Quick Reference
Socket detects issues across five categories:
Supply Chain Risk (most critical for upgrades)
- malware — Known malicious package
- didYouMean — Possible typosquat (similar name to popular package)
- gptMalware — AI-detected malware
- troll — Protestware or potentially unwanted behavior
- obfuscatedFile — Obfuscated code detected
- installScripts — Pre/post-install scripts present
- manifestConfusion — Mismatch between package.json and tarball
Vulnerability
- criticalCVE, cve, mediumCVE, mildCVE — Known CVEs by severity
Quality
- deprecated — Package is deprecated
- unmaintained — No recent maintenance activity
- unpopularPackage — Very low download counts
Maintenance
- Part of quality category; signals inactive packages
License
- noLicenseFound — No license file detected
- copyleftLicense — Copyleft license may restrict usage
- nonpermissiveLicense — Non-permissive terms
Proactive Upgrade Workflow with Socket CLI
Integrate Socket into every stage of dependency management:
1. PRE-UPGRADE: socket scan create --report → baseline scan
2. EVALUATE: socket package score npm <pkg>@<ver> → assess target safety
3. SAFE INSTALL: socket npm install <pkg> → block malicious packages
4. POST-UPGRADE: socket scan create --report → verify no regressions
5. DIFF: socket scan diff <before> <after> → see exactly what changed
6. FIX: socket fix --minimum-release-age 7d → auto-fix any new CVEs
7. OPTIMIZE: socket optimize → apply security overridesQuick Reference: Command Cheat Sheet
| Command | Purpose | Auth Required |
|---|---|---|
socket npm install <pkg> | Install with malware check | No (beta) |
socket npx <cmd> | Run with malware check | No (beta) |
socket wrapper on | Auto-protect all npm/npx | No |
socket package shallow npm <pkg> | Quick package score | Yes |
socket package score npm <pkg> | Deep score (with transitives) | Yes |
socket scan create --report | Full project scan | Yes |
socket scan diff <id1> <id2> | Compare two scans | Yes |
socket ci | CI gate (scan + policy check) | Yes |
socket fix | Auto-fix CVEs | Yes |
socket fix --minimum-release-age 7d | Fix with cooldown alignment | Yes |
socket fix --no-major-updates | Fix without major bumps | Yes |
socket fix --no-apply-fixes | Preview fixes without applying | Yes |
socket fix --autopilot | CI auto-fix with PR creation | Yes |
socket optimize | Apply security overrides | Yes |
socket login | Store API token locally | — |
socket scan setup | Create socket.json defaults | — |
Supported Ecosystems
Socket CLI supports:
- JavaScript/TypeScript: npm, pnpm (v6+), Yarn (classic + berry), Bun
- Python: pip, uv (requirements.txt, uv.lock)
- Java: Maven, Gradle (with gradle.lockfile)
- Ruby: RubyGems
- Go: go.sum/go.mod
- Rust: Cargo
- C#: NuGet (packages.lock.json coming soon)
socket npm / socket npx wrappers only work with npm. For other package managers, use socket scan create for analysis and socket fix for remediation.
Staged Upgrade Strategies
Detailed guide for incremental dependency upgrades with codemod automation.
Upgrade Planning
Phase 1: Assessment
# Identify current versions
npm list --depth=0
bun pm ls # Bun
# Check for available updates
npm outdated
bun outdated
# Check for breaking changes
# Read CHANGELOG.md, MIGRATION.md, GitHub releasesPhase 2: Ordering
Upgrade in dependency order — foundations first:
1. TypeScript / build tools
2. Core framework (React, Next.js, etc.)
3. Routing
4. State management
5. UI libraries
6. Testing libraries
7. Dev dependenciesPhase 3: Execution
Upgrade one major version at a time with testing between each:
# Step 1: Create feature branch
git checkout -b upgrade/react-19
# Step 2: Upgrade the package
bun add react@19 react-dom@19
# Step 3: Test immediately
bun test
bunx tsc --noEmit
bun run build
# Step 4: Fix issues, commit
git add -A && git commit -m "chore: upgrade react to 19"
# Step 5: Continue to next packageCodemod Automation
react-codeshift
# Install codemod runner
bunx react-codeshift <transform> <path>
# Example: rename unsafe lifecycles
bunx react-codeshift \
--parser tsx \
--transform react-codeshift/transforms/rename-unsafe-lifecycles.js \
src/
# Example: update context API
bunx react-codeshift \
--transform react-codeshift/transforms/old-context-apis.js \
src/jscodeshift (generic)
# Run any codemod
bunx jscodeshift -t <codemod-url> src/
# Example: upgrade React Router imports
bunx jscodeshift -t https://raw.githubusercontent.com/ReactTraining/react-router/main/packages/react-router/codemods/5.x-6.x.ts src/Next.js Codemods
# Built-in Next.js upgrade codemods
bunx @next/codemod@latest upgrade
# Specific transforms
bunx @next/codemod new-link src/
bunx @next/codemod next-image-to-legacy-image src/Custom Migration Scripts
AST-Based Transformation
// migration-script.mjs
import { readFileSync, writeFileSync } from 'fs';
import { globSync } from 'glob';
import jscodeshift from 'jscodeshift';
const files = globSync('src/**/*.{ts,tsx}');
for (const file of files) {
const source = readFileSync(file, 'utf8');
const j = jscodeshift(source);
// Example: replace old import with new
j.find(jscodeshift.ImportDeclaration, {
source: { value: 'old-package' }
}).forEach(path => {
path.value.source.value = 'new-package';
});
const output = j.toSource();
if (output !== source) {
writeFileSync(file, output);
console.log(`Updated: ${file}`);
}
}Regex-Based Quick Fixes
// quick-fix.mjs
import { readFileSync, writeFileSync } from 'fs';
import { globSync } from 'glob';
const replacements = [
[/from 'old-api'/g, "from 'new-api'"],
[/oldFunction\(/g, 'newFunction('],
[/import \{ OldComponent \}/g, 'import { NewComponent }'],
];
const files = globSync('src/**/*.{ts,tsx}');
let totalChanges = 0;
for (const file of files) {
let content = readFileSync(file, 'utf8');
let changed = false;
for (const [pattern, replacement] of replacements) {
const matchCount = (content.match(pattern) || []).length;
if (matchCount > 0) {
content = content.replace(pattern, replacement);
changed = true;
totalChanges += matchCount;
}
}
if (changed) {
writeFileSync(file, content);
console.log(`Updated: ${file}`);
}
}
console.log(`Total changes: ${totalChanges}`);Peer Dependency Handling
# npm 7+: strict peer dependencies by default
npm install --legacy-peer-deps # Ignore peer dep conflicts
npm install --force # Override conflicts
# Better approach: resolve the actual conflict
npm ls <package-name> # Find why peer dep is wrong
# Bun: handles peer deps automatically
bun install # Resolves peer deps without flagsWorkspace Upgrades
# Update all workspace packages
npm install --workspaces
# Update specific workspace
bun add package@latest --workspace=packages/app
# Update shared dev dependency across workspaces
bun add -D typescript@latest --workspace=*Rollback Strategy
#!/bin/bash
# rollback.sh
# Save current state
git stash
git checkout -b upgrade-branch
# Attempt upgrade
bun add package@latest
# Run tests
if bun test && bun run build; then
echo "Upgrade successful"
git add package.json bun.lock
git commit -m "chore: upgrade package"
else
echo "Upgrade failed, rolling back"
git checkout main
git branch -D upgrade-branch
bun install # Restore from lockfile
fiSupply Chain Security Reference
Comprehensive guide to preventing supply chain attacks during dependency installation and upgrades.
Threat Landscape
Recent supply chain incidents demonstrate why hardening dependency workflows is critical:
| Incident | Year | Attack Vector | Impact |
|---|---|---|---|
| event-stream | 2018 | Malicious maintainer | Stole Bitcoin wallets |
| eslint-scope | 2018 | Stolen credentials | Published malicious versions |
| ua-parser-js | 2021 | Account takeover | Cryptomining + credential theft |
| node-ipc | 2022 | Protestware | Deleted files on Russian IPs |
| colors/faker | 2022 | Maintainer sabotage | Infinite loop DOS |
| Shai-Hulud | 2024 | Postinstall worm | Propagated via lifecycle scripts |
| Nx | 2025 | Compromised postinstall | Weaponized AI coding agents |
Security Principles
1. Trust Nothing by Default
Every package — direct or transitive — is a potential attack vector. Configure package managers to block unsafe behavior and only allow what is explicitly reviewed.
2. Delay Before Installing
Newly published packages may contain malicious code discovered within hours or days. A cooldown period lets the community catch threats before they reach production.
3. Freeze and Validate
Lockfiles are the contract between development and production. Protect them from injection and validate their integrity in CI.
4. Minimize Attack Surface
Fewer dependencies = fewer potential vulnerabilities. Each transitive dependency inherits all risks of its own dependency tree.
5. Audit Before Trusting
Don't rely solely on npmjs.org — the displayed source code can differ from the actual tarball. Use security tools to audit packages before installation.
Disabling Post-Install Scripts
Post-install scripts are the most common supply chain attack vector. They execute arbitrary code during npm install with full system access.
npm
# Global config (recommended — applies to all projects)
npm config set ignore-scripts true
npm config set allow-git none
# Per-command
npm install --ignore-scripts --allow-git=none <package>
# .npmrc file
ignore-scripts=true
allow-git=noneEven with --ignore-scripts, git-based dependencies can ship .npmrc files that re-enable lifecycle scripts. Use --allow-git=none (npm CLI 11.10.0+) alongside --ignore-scripts to fully close this vector.
Bun
Bun disables postinstall scripts by default and maintains an internal allow-list. Allow specific packages via trustedDependencies in package.json:
{
"trustedDependencies": [
"esbuild",
"sharp"
]
}pnpm (10.0+)
pnpm disables postinstall scripts by default since v10.0. Control which packages can run build scripts:
# pnpm-workspace.yaml
# Preferred (pnpm 10.26+): single map of package → true/false
allowBuilds:
esbuild: true
fsevents: true
nx@21.6.4 || 21.6.5: true
core-js: false
# Legacy (still supported):
# onlyBuiltDependencies:
# - esbuild
# - fsevents
# Make unreviewed scripts a hard error (pnpm 10.3+)
strictDepBuilds: trueAllowlist with @lavamoat/allow-scripts
For projects that need some post-install scripts, use @lavamoat/allow-scripts to create an auditable allowlist:
npm install --save-dev @lavamoat/allow-scripts
npx allow-scripts autoThis scans the dependency tree and creates a package.json entry listing which packages are permitted to run scripts, blocking all others.
Lockfile Injection Prevention
The Threat
Malicious actors can submit PRs that modify lockfiles to point to compromised packages or change the resolved URL + integrity hash to their own payload.
Validation with lockfile-lint
npm install --save-dev lockfile-lint
# Validate npm lockfile
npx lockfile-lint \
--path package-lock.json \
--type npm \
--allowed-hosts npm yarn \
--validate-https
# Validate yarn lockfile
npx lockfile-lint \
--path yarn.lock \
--type yarn \
--allowed-hosts npm yarn \
--validate-httpsCI Integration
{
"scripts": {
"lint:lockfile": "lockfile-lint --path package-lock.json --type npm --allowed-hosts npm --validate-https",
"preinstall": "npm run lint:lockfile"
}
}pnpm Specific Hardening
pnpm is inherently more resistant to lockfile injection:
- Doesn't maintain tarball sources that can be maliciously modified
- Won't install packages in the lockfile that aren't in
package.json
Additional protection (pnpm 10.26+):
# pnpm-workspace.yaml
# Block transitive deps from using exotic sources (git repos, tarball URLs)
blockExoticSubdeps: trueTrust Policy (pnpm 10.21+)
Detect when a package's publish-time trust level has decreased — an early signal of account compromise:
# pnpm-workspace.yaml
trustPolicy: no-downgrade
# Allow specific packages to bypass
trustPolicyExclude:
- 'chokidar@4.0.3'
# Ignore check for packages published >30 days ago (pnpm 10.27+)
trustPolicyIgnoreAfter: 43200 # minutes (30 days)Trust levels (strongest → weakest): 1. Trusted Publisher — published via OIDC/GitHub Actions 2. Provenance — published with npm provenance attestation 3. Signatures — registry signature present 4. No evidence — no trust signals
Bun Lockfile Notes
Bun uses bun.lock (text-based, default since v1.2) or bun.lockb (binary). lockfile-lint does not currently support Bun lockfile formats. Monitor the lockfile-lint repo for future support.
Pre-Install Security Auditing
npq — Pre-Install Package Auditor
npq audits packages before installation using multiple security "marshalls":
# Install globally
npm install -g npq
# Use instead of npm
npq install express
# Dry run (audit without installing)
npq install express --dry-run
# Shell alias for seamless integration
alias npm='npq-hero'
echo "alias npm='npq-hero'" >> ~/.zshrc
# Use with other package managers
NPQ_PKG_MGR=pnpm npq install fastify
NPQ_PKG_MGR=bun npq install fastify
# Permanent aliases for other PMs
alias pnpm="NPQ_PKG_MGR=pnpm npq-hero"What npq validates:
- Vulnerability scanning (Snyk CVE database)
- Package age analysis (flags packages < 22 days old)
- Typosquatting detection
- Registry signature verification
- Provenance attestation checks
- Pre/post-install script warnings
- Package health (README, LICENSE, repo URL, downloads)
- Binary introduction warnings
- Deprecation status
- Maintainer domain validation (expired domains)
Socket Firewall (sfw) — Real-Time Package Firewall
sfw intercepts package manager commands and blocks malicious packages:
# Install globally
npm install -g sfw
# Prefix any package manager command
sfw npm install express
sfw pnpm add express
sfw yarn add express
sfw pip install requests
sfw cargo fetchWhat sfw checks:
- Malicious code detection
- Install script risks
- Typosquatting detection
- Dependency confusion attacks
- Known vulnerabilities
- Protestware and env variable access
- Network and filesystem access patterns
Comparison
| Feature | npq | sfw | Socket CLI (socket npm) |
|---|---|---|---|
| Analysis | Pre-install marshalls | Real-time deep analysis | Full transitive scan via npm integration |
| Data sources | Snyk CVE, npm metadata | Socket proprietary intelligence | Socket proprietary intelligence |
| Interactivity | Prompts before install | Blocks and prompts flagged packages | Prompts before installing flagged packages |
| PM support | npm, pnpm, Bun (env vars) | npm, yarn, pnpm, pip, uv, cargo | npm, npx (wrapper mode) |
| Open source | Yes | Client only | Client only (open source on GitHub) |
| Free tier | Yes | No | Yes (beta, default issues only) |
| CI integration | No | No | Yes (socket ci, socket fix --autopilot) |
| Package scoring | No | No | Yes (socket package score) |
| CVE auto-fixing | No | No | Yes (socket fix) |
| Requires auth | No | Yes | No for wrapper, Yes for scans/fix |
See references/socket-cli-guide.md for full Socket CLI documentation.
Publisher Security
Enable 2FA for npm Accounts
# Enable for auth + publishing
npm profile enable-2fa auth-and-writes
# Enable for auth only
npm profile enable-2fa auth-onlyPublish with Provenance Attestations
Provides cryptographic proof of where and how packages were built:
# GitHub Actions
permissions:
id-token: write
steps:
- run: npm publish --provenanceRequires npm CLI 9.5.0+ and GitHub Actions or GitLab CI/CD.
Publish with OIDC (Trusted Publishing)
Eliminates long-lived npm tokens by using short-lived OIDC tokens from CI:
# GitHub Actions
permissions:
id-token: write
steps:
- run: npm publishConfigure trusted publisher on npmjs.com first. Automatically generates provenance attestations (OpenSSF compliant).
Avoiding Blind Upgrades
Anti-Patterns
# DANGEROUS — upgrades everything without review
npm update
npx npm-check-updates -uIncidents like colors/faker and node-ipc demonstrate why blind upgrades are dangerous.
Safe Alternatives
# Interactive — review each upgrade
npx npm-check-updates --interactive
# Use automated tools with security policies
# - Snyk: 21-day cooldown built-in
# - Dependabot: configurable cooldown
# - Renovate: minimumReleaseAge configPackage Health Assessment
Snyk Security Database
Before adopting any package, check security.snyk.io:
https://security.snyk.io/package/npm/<package-name>Provides: security vulnerabilities, popularity trends, maintenance activity, community signals.
Don't Trust npmjs.org Alone
The npmjs.org website:
- Omits git and HTTPS-based dependencies from displayed
package.json - Source code display can drift from the actual installed tarball
Always inspect the actual tarball:
npm pack <package-name> --dry-run
npm pack <package-name>
tar -tzf <package-name>-<version>.tgzDependency Tree Reduction
Replace common dependencies with native JavaScript:
// Instead of lodash
const unique = [...new Set(array)];
// Instead of axios
const response = await fetch(url);
// Instead of utility libraries
const isEmpty = obj => Object.keys(obj).length === 0;
const clone = structuredClone(original);Each dependency adds transitive attack surface. Evaluate necessity, maintenance burden, and bundle size before adding any dependency.
Testing Strategy for Dependency Upgrades
Comprehensive testing approaches to validate dependency upgrades at every level.
Testing Pyramid
E2E Tests
/ \
Integration Tests
/ \
Unit Tests
/ \
Static Analysis (tsc, lint)Run from bottom to top after each upgrade.
Level 1: Static Analysis
# TypeScript type-check (fastest feedback)
bunx tsc --noEmit
# Lint
bun run lint
# Check bundle (size impact)
bun run buildLevel 2: Unit Tests
# Run all unit tests
bun test
# Watch mode during upgrade
bun test --watch
# Specific test file
bun test src/components/Button.test.tsxAfter upgrading, check for:
- Type errors from API changes
- Assertion failures from behavior changes
- Missing exports from package restructuring
Level 3: Integration Tests
describe('Dependency Compatibility', () => {
it('should have matching React versions', () => {
const reactVersion = require('react/package.json').version;
const reactDomVersion = require('react-dom/package.json').version;
expect(reactVersion).toBe(reactDomVersion);
});
it('should render without crashing', () => {
const { container } = render(<App />);
expect(container).toBeTruthy();
});
it('should handle navigation', () => {
render(<App />);
fireEvent.click(screen.getByText('Navigate'));
expect(screen.getByText('New Page')).toBeInTheDocument();
});
});Level 4: Visual Regression Tests
describe('Visual Regression', () => {
it('should match snapshot', () => {
const { container } = render(<Component />);
expect(container.firstChild).toMatchSnapshot();
});
it('should match visual baseline', () => {
render(<Dashboard />);
// Compare screenshot against baseline
cy.compareSnapshot('dashboard');
});
});After UI library upgrades, always check:
- Snapshot diffs
- Color/font changes
- Layout shifts
- Responsive breakpoints
Level 5: E2E Tests
// cypress/e2e/app.cy.js
describe('E2E Smoke Tests', () => {
it('should load the app', () => {
cy.visit('/');
cy.get('[data-testid="app"]').should('exist');
});
it('should complete auth flow', () => {
cy.visit('/login');
cy.get('input[name="email"]').type('user@example.com');
cy.get('button[type="submit"]').click();
cy.url().should('include', '/dashboard');
});
it('should handle API errors gracefully', () => {
cy.intercept('GET', '/api/data', { statusCode: 500 });
cy.visit('/dashboard');
cy.get('[data-testid="error-message"]').should('be.visible');
});
});Bundle Analysis
# Compare bundle sizes before and after upgrade
bun run build
# Analyze bundle composition
bunx bundle-analyzer dist/index.js
# Check for unexpected bundle size increases
# Before upgrade: note the bundle size
# After upgrade: compare and investigate increases > 5%Performance Testing
# Run Lighthouse CI
bunx @lhci/cli autorun
# Check Core Web Vitals
# - LCP: Largest Contentful Paint
# - FID: First Input Delay
# - CLS: Cumulative Layout ShiftUpgrade Test Matrix
| Test Type | When to Run | Failure Indicates |
|---|---|---|
| Type-check | After every package | Breaking API changes |
| Unit tests | After every package | Behavior changes |
| Integration | After framework upgrades | Compatibility issues |
| Visual regression | After UI library upgrades | Visual breaking changes |
| E2E | After major upgrades | User flow breakage |
| Bundle analysis | After any upgrade | Size regression |
| Performance | After major upgrades | Performance regression |
CI Integration
# .github/workflows/upgrade-validation.yml
name: Upgrade Validation
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install --frozen-lockfile
- run: bunx tsc --noEmit
- run: bun run lint
- run: bun test
- run: bun run build
- run: bun run test:e2e404: Not Found# bunfig.toml — Security-Hardened Configuration for Bun
# Copy to project root as bunfig.toml
# Customize values marked with [CONFIGURE]
[install]
# === COOLDOWN PERIOD ===
# Only install package versions published at least 7 days ago (value in seconds)
# 3 days = 259200, 7 days = 604800, 14 days = 1209600, 21 days = 1814400
minimumReleaseAge = 604800
# Packages that bypass the cooldown (typically build tooling that needs fast patches)
# [CONFIGURE] Add packages that need same-day updates
minimumReleaseAgeExcludes = [
"@types/bun",
"typescript",
"esbuild"
]
# === REGISTRY ===
registry = "https://registry.npmjs.org/"
# === PEER DEPENDENCIES ===
# Bun handles peer deps automatically; no configuration needed
# .github/dependabot.yml — Automated Updates with Cooldown
# Copy to .github/dependabot.yml
# Customize values marked with [CONFIGURE]
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
# [CONFIGURE] How often to check for updates: daily, weekly, biweekly, monthly
interval: "weekly"
day: "monday"
# Wait 7 days after a new version is published before creating a PR
cooldown:
default-days: 7
open-pull-requests-limit: 5
# [CONFIGURE] Add reviewers
# reviewers:
# - "team-leads"
commit-message:
prefix: "chore"
include: "scope"
labels:
- "dependencies"
# [CONFIGURE] Uncomment to automerge minor/patch updates
# ignore:
# - dependency-name: "*"
# update-types: ["version-update:semver-major"]
# [CONFIGURE] Uncomment for GitHub Actions updates
# - package-ecosystem: "github-actions"
# directory: "/"
# schedule:
# interval: "weekly"
# cooldown:
# default-days: 7
{
"name": "Node.js Hardened Dev Container",
"image": "mcr.microsoft.com/devcontainers/javascript-node:20",
"features": {
"ghcr.io/devcontainers/features/1password:1": {}
},
"runArgs": [
"--security-opt=no-new-privileges:true",
"--cap-drop=ALL",
"--cap-add=CHOWN",
"--cap-add=SETUID",
"--cap-add=SETGID"
],
"containerEnv": {
"NODE_OPTIONS": "--disable-proto=delete"
},
"postCreateCommand": "npm ci --ignore-scripts",
"customizations": {
"vscode": {
"extensions": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
]
}
},
"remoteUser": "node"
}
# .npmrc — Security-Hardened Configuration
# Copy to project root as .npmrc
# Customize values marked with [CONFIGURE]
# === POST-INSTALL SCRIPTS ===
# Block all lifecycle scripts (postinstall, preinstall, etc.)
ignore-scripts=true
# Block git-based dependencies from re-enabling scripts (npm CLI 11.9.0+)
allow-git=none
# === COOLDOWN PERIOD ===
# Only install package versions published at least 7 days ago
# Adjust value (in days): 3 (aggressive), 7 (recommended), 14 (conservative)
min-release-age=7
# === REGISTRY ===
# Use official npm registry
registry=https://registry.npmjs.org/
# === SECURITY ===
# Require integrity checks on all packages
strict-ssl=true
# === OPTIONAL: SCRIPT ALLOWLIST ===
# To allow specific packages to run scripts, use @lavamoat/allow-scripts
# npx allow-scripts auto
# Then review and approve in package.json
# pnpm-workspace.yaml — Security-Hardened Configuration
# Copy to project root as pnpm-workspace.yaml
# Requires pnpm 10.0+ (most features) / 10.16+ (cooldown) / 10.21+ (trust policy) / 10.26+ (allowBuilds, blockExoticSubdeps)
# Customize values marked with [CONFIGURE]
# === COOLDOWN PERIOD ===
# Only install package versions published at least 7 days ago (value in minutes)
# 3 days = 4320, 7 days = 10080, 14 days = 20160, 21 days = 30240
minimumReleaseAge: 10080
# Packages that bypass the cooldown
# [CONFIGURE] Add packages that need same-day updates
minimumReleaseAgeExclude:
- "@types/react"
- "@types/node"
- typescript
- esbuild
# === POST-INSTALL SCRIPTS ===
# Preferred (pnpm 10.26+): map of package → true/false
# [CONFIGURE] Add packages that genuinely need build scripts
allowBuilds:
esbuild: true
fsevents: true
# sharp: true
# core-js: false
# Make unreviewed build scripts a hard error (pnpm 10.3+)
strictDepBuilds: true
# === TRUST POLICY (pnpm 10.21+) ===
# Fail if a package's trust level has decreased vs a previous version
trustPolicy: no-downgrade
# [CONFIGURE] Exclude specific packages from trust checks
# trustPolicyExclude:
# - "chokidar@4.0.3"
# Ignore trust checks for packages published >30 days ago (pnpm 10.27+)
trustPolicyIgnoreAfter: 43200 # minutes (30 days)
# === EXOTIC DEPENDENCY BLOCKING (pnpm 10.26+) ===
# Prevent transitive deps from using git repos or tarball URLs
blockExoticSubdeps: true
# === WORKSPACES (if using monorepo) ===
# [CONFIGURE] Uncomment and adjust for monorepo
# packages:
# - "apps/*"
# - "packages/*"
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:base"],
"minimumReleaseAge": "7 days",
"schedule": [
"before 3am on Monday"
],
"timezone": "America/New_York",
"packageRules": [
{
"description": "Automerge minor and patch updates after cooldown",
"matchUpdateTypes": ["minor", "patch"],
"automerge": true,
"minimumReleaseAge": "7 days"
},
{
"description": "Require manual review for major updates, longer cooldown",
"matchUpdateTypes": ["major"],
"automerge": false,
"minimumReleaseAge": "14 days",
"labels": ["major-update", "review-required"]
},
{
"description": "Skip cooldown for build tooling that needs fast patches",
"matchPackageNames": ["typescript", "esbuild"],
"minimumReleaseAge": null
}
],
"lockFileMaintenance": {
"enabled": true,
"schedule": ["before 3am on Monday"]
},
"vulnerabilityAlerts": {
"enabled": true
},
"dependencyDashboard": true,
"dependencyDashboardTitle": "Dependency Dashboard"
}
# Socket Fix CI Workflow
# Automatically fixes CVEs with Socket and creates PRs
# Runs twice daily. Complements Dependabot/Renovate.
# Requires: SOCKET_CLI_API_TOKEN secret in GitHub repo/org settings
#
# Docs: https://docs.socket.dev/docs/socket-fix
name: socket-fix
on:
schedule:
- cron: '0 0 * * *'
- cron: '0 12 * * *'
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
socket-fix:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
# -- uncomment and adjust for your package manager --
# - name: Setup pnpm
# uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda
# with:
# version: '^10.16.0'
# - name: Setup Node.js
# uses: actions/setup-node@v4
# with:
# node-version: "22"
# cache: 'pnpm'
- name: Install dependencies
run: npm ci
- name: Run Socket Fix
env:
SOCKET_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SOCKET_CLI_GIT_USER_EMAIL: socket-fix[bot]@users.noreply.github.com
SOCKET_CLI_GIT_USER_NAME: socket-fix[bot]
SOCKET_CLI_API_TOKEN: ${{ secrets.SOCKET_CLI_API_TOKEN }}
run: npx @socketsecurity/cli fix --autopilot --minimum-release-age 7d
# Socket CI Security Gate
# Blocks PRs that violate your organization's security policy
# Runs on every push and pull request
#
# Requires: SOCKET_SECURITY_API_KEY secret in GitHub repo/org settings
# Docs: https://docs.socket.dev/docs/socket-ci
name: socket-security
on:
push:
branches: ['**']
pull_request:
types: [opened, synchronize, reopened]
concurrency:
group: socket-scan-${{ github.ref }}-${{ github.sha }}
cancel-in-progress: true
jobs:
socket-ci:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "22"
- name: Install Socket CLI
run: npm install -g socket
- name: Run Socket CI Gate
env:
SOCKET_SECURITY_API_KEY: ${{ secrets.SOCKET_SECURITY_API_KEY }}
run: socket ci
# .yarnrc.yml — Security-Hardened Configuration for Yarn 4.10+
# Copy to project root as .yarnrc.yml
# Customize values marked with [CONFIGURE]
# === COOLDOWN PERIOD ===
# Only consider npm package versions published at least 7 days ago
# Value is in minutes (10080 = 7 days, 43200 = 30 days)
npmMinimalAgeGate: 10080
# Packages that bypass the age gate (descriptors or glob patterns)
# [CONFIGURE] Add packages that need same-day updates
npmPreapprovedPackages:
- "@types/react"
- "@types/node"
- typescript
- esbuild
# === REGISTRY ===
npmRegistryServer: "https://registry.npmjs.org/"
# === SECURITY ===
# Require integrity checks
unsafeHttpWhitelist: []
# === DETERMINISTIC INSTALLS ===
# In CI, use: yarn install --immutable --immutable-cache
Related skills
How it compares
Use dependency-upgrade when supply-chain policy and staged testing matter; use lighter dev-dependency-management skills for basic version bumps only.
FAQ
What is dependency-upgrade default cooldown?
dependency-upgrade recommends a 7-day min-release-age cooldown so newly published packages are vetted before install. npm uses min-release-age=7 in .npmrc; Bun uses minimumReleaseAge=604800 seconds; pnpm uses 10080 minutes.
Which package managers does dependency-upgrade support?
dependency-upgrade covers npm, Bun, pnpm, Yarn, and Deno with per-manager cooldown and script-blocking configs. It also documents frozen install commands like npm ci, bun install --frozen-lockfile, and pnpm install --frozen-lockfile for CI.