Skip to main content

npm-security-best-practices

Expert guidance on securing npm packages, preventing supply chain attacks, and hardening package manager configurations

Datos de origen

Repositorio
reason-machines/security-skills
Última actividad en el origen
20 de mayo de 2026 a las 00:54
Idioma detectado de SKILL.md
inglés
Estrellas
12
Forks
1

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
npm-security-best-practices
description
Expert guidance on securing npm packages, preventing supply chain attacks, and hardening package manager configurations
triggers
["how do I secure my npm dependencies","protect against supply chain attacks in npm","configure npm security settings","disable npm postinstall scripts","block malicious npm packages","set up secure package manager config","prevent dependency confusion attacks","harden npm package installs"]
# npm Security Best Practices > Skill by [ara.so](https://ara.so) — Security Skills collection. This skill provides expert guidance on securing npm package installations, preventing supply chain attacks, and implementing security best practices for Node.js development. Based on the comprehensive npm-security-best-practices repository by Lirantal. ## Overview The npm ecosystem is a frequent target for supply chain attacks including: - **Shai-Hulud attacks** - Worm-like propagation through compromised packages - **Nx incident** - Malicious code in postinstall scripts - **event-stream attack** - Long-running exfiltration via lifecycle scripts - **Dependency confusion** - Attackers publishing malicious packages with internal names This skill covers configuration, tooling, and practices to mitigate these risks across npm, pnpm, and Bun. ## Secure-by-Default Configuration ### npm (.npmrc) Create or update `.npmrc` in your project root or global config (`~/.npmrc`): ```ini # Disable all lifecycle scripts (postinstall, preinstall, etc.) ignore-scripts=true # Block git-based dependencies (git+ssh://, git+https://, etc.) allow-git=none # Only install packages that have been published for at least 30 days min-release-age=30 ``` Apply globally: ```bash npm config set ignore-scripts true npm config set allow-git none npm config set min-release-age 30 ``` ### pnpm (pnpm-workspace.yaml) Create `pnpm-workspace.yaml` in your project root: ```yaml # Block packages newer than 30 days (43200 minutes) minimumReleaseAge: 43200 # Reject versions with regressed trust signals (pnpm 10.21+) trustPolicy: no-downgrade # Allowlist for packages that need build scripts allowBuilds: esbuild: true rolldown: true nx@21.6.4 || 21.6.5: true # Fail install if unlisted scripts try to run strictDepBuilds: true # Block git URLs in dependencies blockExoticSubdeps: true # Optional: ignore trust policy for legacy packages trustPolicyIgnoreAfter: 43200 # Optional: exempt specific packages from trust policy trustPolicyExclude: - 'chokidar@4.0.3' ``` ### Bun (package.json) Bun disables postinstall scripts by default. To allow specific packages: ```json { "trustedDependencies": [ "esbuild", "sharp", "fsevents" ] } ``` ## Installation Commands ### Secure npm install ```bash # Install with security flags npm install --ignore-scripts --allow-git=none # Use npm ci for reproducible installs (CI/CD) npm ci --ignore-scripts # Install specific package securely npm install lodash --ignore-scripts --allow-git=none ``` ### Secure pnpm install ```bash # Install with workspace config enforced pnpm install # Review blocked packages pnpm install --loglevel=verbose # Bypass trust policy for specific install (use sparingly) pnpm install --ignore-trust-policy ``` ### Selective script execution Use `@lavamoat/allow-scripts` for granular control: ```bash # Install the tool npm install -g @lavamoat/allow-scripts # Create allowlist npx allow-scripts setup # Edit package.json to add allowlist ``` Example `package.json`: ```json { "scripts": { "prepare": "allow-scripts" }, "lavamoat": { "allowScripts": { "esbuild": true, "core-js": false, "nx>@nx/nx-linux-x64-gnu": true } } } ``` ## Hardening npx Execution ```bash # Execute without installing package npx --no-install create-react-app my-app # Use specific version to avoid typosquatting npx create-react-app@5.0.1 my-app # Verify package before running npm view create-react-app npx create-react-app my-app # Use with security wrappers (see below) ``` ## Security Tooling Integration ### npq - Package quality and security checks ```bash # Install globally npm install -g npq # Use instead of npm install npq install lodash # Check package before installing npq check lodash ``` ### Socket Firewall (sfw) ```bash # Install globally npm install -g @socketsecurity/cli # Protect npm install npx @socketsecurity/cli npm install # Audit project dependencies npx @socketsecurity/cli audit # Check specific package npx @socketsecurity/cli info lodash ``` ### Snyk - Vulnerability scanning ```bash # Install CLI npm install -g snyk # Authenticate snyk auth # Test for vulnerabilities snyk test # Monitor project snyk monitor # Check for supply chain issues snyk code test ``` ## Automated Dependency Updates with Cooldown ### Renovate Bot (renovate.json) ```json { "extends": ["config:base"], "minimumReleaseAge": "30 days", "stabilityDays": 30, "prCreation": "not-pending", "packageRules": [ { "matchUpdateTypes": ["major"], "minimumReleaseAge": "60 days" } ] } ``` ### Dependabot (.github/dependabot.yml) ```yaml version: 2 updates: - package-ecosystem: "npm" directory: "/" schedule: interval: "weekly" # Dependabot doesn't support minimumReleaseAge natively # Use branch protection rules and manual delay open-pull-requests-limit: 10 labels: - "dependencies" - "security" ``` Use GitHub Actions to enforce cooldown: ```yaml name: Dependency Cooldown Check on: pull_request: paths: - 'package.json' - 'package-lock.json' jobs: check-release-age: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Check package age run: | # Custom script to verify package age node scripts/check-package-age.js ``` ## Lockfile Security ### Prevent lockfile injection ```bash # Review lockfile changes in PRs git diff package-lock.json # Verify integrity npm audit # Regenerate if suspicious rm package-lock.json npm install --ignore-scripts ``` ### .gitattributes protection ``` # Prevent merge conflicts from hiding malicious changes package-lock.json merge=binary pnpm-lock.yaml merge=binary ``` ## Package Health Assessment ### Snyk Advisor lookup ```bash # Check package health curl https://snyk.io/advisor/npm-package/lodash | jq # CLI check npx snyk-advisor lodash ``` ### Manual verification checklist ```javascript // check-package-health.js const fetch = require('node-fetch'); async function checkPackage(packageName) { const response = await fetch(`https://registry.npmjs.org/${packageName}`); const data = await response.json(); const latestVersion = data['dist-tags'].latest; const versionInfo = data.versions[latestVersion]; console.log(`Package: ${packageName}@${latestVersion}`); console.log(`Published: ${versionInfo.time || 'N/A'}`); console.log(`Maintainers: ${data.maintainers?.length || 0}`); console.log(`License: ${versionInfo.license || 'NONE'}`); console.log(`Has scripts: ${!!versionInfo.scripts}`); console.log(`Dependencies: ${Object.keys(versionInfo.dependencies || {}).length}`); // Check provenance (npm 9.5+) if (versionInfo.dist?.attestations) { console.log('✓ Has provenance attestation'); } else { console.log('⚠ No provenance attestation'); } } checkPackage(process.argv[2]); ``` Run: ```bash node check-package-health.js lodash ``` ## Preventing Dependency Confusion ### .npmrc scoped registries ```ini # Use private registry for org packages @mycompany:registry=https://npm.pkg.github.com //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN} # Public packages from npm registry=https://registry.npmjs.org/ ``` ### package.json name scoping ```json { "name": "@mycompany/internal-lib", "version": "1.0.0", "publishConfig": { "registry": "https://npm.pkg.github.com" } } ``` ## Dev Container Security ### .devcontainer/devcontainer.json ```json { "name": "Secure Node.js Dev", "image": "mcr.microsoft.com/devcontainers/javascript-node:20", "features": { "ghcr.io/devcontainers/features/node:1": { "version": "20" } }, "postCreateCommand": "npm config set ignore-scripts true && npm config set allow-git none", "remoteEnv": { "NPM_CONFIG_IGNORE_SCRIPTS": "true", "NPM_CONFIG_ALLOW_GIT": "none" }, "mounts": [ "source=${localEnv:HOME}/.npmrc,target=/home/node/.npmrc,type=bind,consistency=cached" ] } ``` ## Environment Variable Security ### Use .env.vault instead of plaintext .env ```bash # Install dotenv-vault npm install dotenv-vault # Encrypt secrets npx dotenv-vault local build # Generate .env.vault (encrypted) npx dotenv-vault push # .gitignore update echo ".env" >> .gitignore echo ".env.*.vault" >> .gitignore ``` ### Access encrypted secrets ```javascript // app.js require('dotenv-vault-core').config(); const apiKey = process.env.API_KEY; // Decrypted at runtime ``` ## Maintainer Best Practices ### Enable 2FA on npm account ```bash # Enable 2FA npm profile enable-2fa auth-and-writes # Verify status npm profile get ``` ### Publish with provenance (npm 9.5+) ```bash # Publish with provenance attestation npm publish --provenance # Verify in package.json before publishing npm pack --dry-run ``` ### GitHub Actions OIDC publishing ```yaml # .github/workflows/publish.yml name: Publish Package on: release: types: [created] jobs: publish: runs-on: ubuntu-latest permissions: id-token: write contents: read steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' registry-url: 'https://registry.npmjs.org' - run: npm ci --ignore-scripts - run: npm publish --provenance --access public env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} ``` ## Troubleshooting ### Scripts are blocked but package needs them **Problem**: A legitimate package requires postinstall scripts. **Solution**: Use allowlist approach with `@lavamoat/allow-scripts` or pnpm `allowBuilds`: ```bash # pnpm approach # Add to pnpm-workspace.yaml allowBuilds: problematic-package: true # npm/yarn approach with lavamoat npm install @lavamoat/allow-scripts npx allow-scripts setup # Edit package.json lavamoat.allowScripts section ``` ### Git dependency is required **Problem**: Internal package only available via git URL. **Solution**: Use private npm registry instead: ```bash # Publish to GitHub Packages echo "//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}" >> .npmrc npm publish --registry=https://npm.pkg.github.com # Or use Verdaccio for self-hosted registry npm install -g verdaccio verdaccio ``` ### Cooldown period blocks urgent security fix **Problem**: Security patch released but blocked by min-release-age. **Solution**: Temporarily override for specific package: ```bash # npm - install specific version without min-release-age npm install package@1.2.3 --no-min-release-age # pnpm - add to trustPolicyExclude # In pnpm-workspace.yaml: trustPolicyExclude: - 'package@1.2.3' ``` ### Trust policy blocks legitimate package **Problem**: pnpm trust policy rejects package downgrade. **Solution**: Investigate first, then exempt if safe: ```bash # Check what changed npm view package@version dist.integrity npm view package@old-version dist.integrity # If legitimate, exempt in pnpm-workspace.yaml trustPolicyExclude: - 'package@version' ``` ### False positive from security scanner **Problem**: Snyk/Socket reports issue in vetted package. **Solution**: Create exceptions with justification: ```yaml # .snyk ignore: SNYK-JS-LODASH-12345: - lodash: reason: 'Prototype pollution not exploitable in our use case' expires: '2024-12-31' ``` ## CI/CD Integration ### GitHub Actions security workflow ```yaml name: Security Audit
Ver en GitHub
Este SKILL.md es muy grande, por eso SkillsMP muestra aqui solo la primera seccion. Ver en GitHub