A security tool to prevent supply chain attacks by enforcing package age requirements for new and updated npm dependencies.
npm-cooldown protects your projects from potential supply chain attacks by ensuring that newly added or updated packages have been available for a minimum period before installation. This "cooling-off" period helps avoid packages that might contain malicious code that hasn't been discovered yet.
- 🔒 Supply Chain Protection: Blocks installation of packages that are too new
- 📊 Multiple Lockfile Support: Works with npm, yarn, and pnpm lockfiles
- 🎯 Flexible Comparisons: Compare local files, git HEAD, or specific commits
- ⚙️ Configurable Age Requirements: Set custom minimum package age in days
- 🔍 Dependency Graph Analysis: Uses Snyk's dependency graph parser for accurate analysis
- 📝 Comprehensive Logging: Clear feedback on what packages are blocked and why
- 🧪 Effect.js Integration: Built with Effect.js for robust error handling and composition
npm install -g npm-cooldownOr use with npx:
npx npm-cooldown# Compare local package.json/lockfile with git HEAD
npm-cooldown
# Set minimum age requirement to 7 days
npm-cooldown --min-age 7
# Compare specific git revisions
npm-cooldown --source head --target local
npm-cooldown --source abc123 --target def456| Option | Description | Default |
|---|---|---|
--min-age <days> |
Minimum package age in days | 3 |
--source <revision> |
Source revision: 'local', 'head', or commit hash | 'local' |
--target <revision> |
Target revision: 'local', 'head', or commit hash | 'head' |
--lockfile <path> |
Path to lockfile (auto-detected if not specified) | Auto-detected |
--manifest <path> |
Path to package.json | './package.json' |
--registry <url> |
NPM registry URL | 'https://registry.npmjs.org' |
--help |
Show help message |
# In your CI pipeline, compare what's in the repository with local changes
npm-cooldown --source head --target local --min-age 7# Check if any new dependencies are too fresh
npm-cooldown --min-age 3# Works automatically with yarn.lock
npm-cooldown --min-age 5
# Works automatically with pnpm-lock.yaml
npm-cooldown --min-age 5- Detects Lockfile Type: Automatically identifies whether you're using npm, yarn, or pnpm
- Reads Source & Target Files: Retrieves lockfiles and manifests from filesystem or git
- Parses Dependency Graphs: Uses Snyk's lockfile parser to build dependency graphs
- Compares Dependencies: Identifies added or updated packages between revisions
- Checks Package Ages: Queries the npm registry for package publish dates
- Validates Age Requirements: Blocks packages that don't meet the minimum age requirement
Add to your package.json scripts:
{
"scripts": {
"preinstall": "npm-cooldown",
"security-check": "npm-cooldown --min-age 7"
}
}# Install husky
npm install --save-dev husky
# Add pre-commit hook
npx husky add .husky/pre-commit "npm-cooldown"name: Security Check
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Full git history needed for comparisons
- uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install npm-cooldown
run: npm install -g npm-cooldown
- name: Check dependency ages
run: npm-cooldown --source origin/main --target HEAD --min-age 7NPM_REGISTRY_URL: Default registry URLTMPDIR: Temporary directory for file operations
Currently, package whitelisting is not implemented via CLI but can be configured programmatically:
import { validatePackages } from 'npm-cooldown';
const config = {
minAgeHours: 72, // 3 days
whitelistedPackages: ['trusted-package', '@company/internal-package'],
registryUrl: 'https://registry.npmjs.org',
};The tool exits with different codes based on the outcome:
- 0: All dependencies pass age requirements
- 1: One or more dependencies are too new or other errors occurred
Common error scenarios:
- Lockfile not found: Ensure your project has a lockfile (package-lock.json, yarn.lock, or pnpm-lock.yaml)
- Git revision not found: Verify the specified commit hash or branch exists
- Registry errors: Check network connectivity and registry URL
- Package not found: Some packages might not exist in the registry
You can also use npm-cooldown programmatically:
import { Effect } from 'effect';
import { getFiles } from 'npm-cooldown/get-file';
import { parseLockfile } from 'npm-cooldown/parse-lockfile';
import { compareDepGraphs } from 'npm-cooldown/compare-dep-graphs';
const program = Effect.gen(function* () {
// Get lockfiles from local and HEAD
const [sourceFiles, targetFiles] = yield* Effect.all([
getFiles('package-lock.json', 'package.json', 'local'),
getFiles('package-lock.json', 'package.json', 'head'),
]);
// Parse dependency graphs
const [sourceGraph, targetGraph] = yield* Effect.all([
parseLockfile(sourceFiles[0], sourceFiles[1], 'package-lock.json'),
parseLockfile(targetFiles[0], targetFiles[1], 'package-lock.json'),
]);
// Compare graphs
const differences = yield* compareDepGraphs(sourceGraph, targetGraph);
return differences;
});
Effect.runPromise(program).then(console.log);- Typosquatting attacks: Malicious packages with names similar to popular packages
- Account takeovers: Compromised maintainer accounts publishing malicious updates
- Dependency confusion: Internal packages being overshadowed by public packages
- Zero-day supply chain attacks: Newly published malicious packages
- Does not scan existing packages: Only checks new/updated dependencies
- Age-based protection only: Does not perform static analysis or vulnerability scanning
- Registry dependency: Requires access to npm registry for age verification
- Bypass potential: Developers can override with direct package installation
- Use in CI/CD: Integrate into your continuous integration pipeline
- Combine with other tools: Use alongside vulnerability scanners and license checkers
- Regular updates: Keep npm-cooldown updated for the latest security improvements
- Team education: Ensure team understands why package age matters
- Emergency procedures: Have a process for urgent package updates when needed
Error: "No lockfile found"
# Ensure you have a lockfile in your project
npm install # Creates package-lock.json
# or
yarn install # Creates yarn.lock
# or
pnpm install # Creates pnpm-lock.yamlError: "Git revision not found"
# Make sure you're in a git repository and the revision exists
git status
git log --oneline -n 5Error: "Failed to fetch metadata"
# Check your network connection and registry access
npm ping
# or use a different registry
npm-cooldown --registry https://your-private-registry.comFor verbose output, you can examine the source code or add logging. The tool uses Effect.js for structured error handling.
- Large projects: Processing time scales with the number of dependencies
- Network dependency: Registry queries can be slow; results are cached
- Git operations: Large repositories may have slower git operations
We welcome contributions! Please see our Contributing Guide for details.
git clone https://github.com/getjerry/npm-cooldown.git
cd npm-cooldown
npm install
npm run build
npm test# Unit tests
npm test
# E2E tests with Verdaccio
npm run test:e2e
# Type checking
npm run typecheck
# Linting
npm run lintISC License - see LICENSE file for details.
See CHANGELOG.md for version history and updates.
If you discover a security vulnerability, please send an email to security@getjerry.com. All security vulnerabilities will be promptly addressed.
- Built with Effect.js for robust functional programming
- Uses Snyk's dependency graph parser for lockfile analysis
- Inspired by security research on npm supply chain attacks
Made with ❤️ by the Jerry Security Team