Supply Chain Security: Dependency Confusion, Typosquatting, and SCA Testing
Software supply chain attacks have moved from theoretical to routine. The SolarWinds compromise, the xz-utils backdoor, the event-stream incident, the countless malicious npm packages published daily — the pattern is consistent: attackers target the components your software trusts rather than your software directly. The attack surface is your dependencies, your build pipeline, and the maintainers who control them.
For a security tester assessing a web application, supply chain security isn't just a background concern — it's an active attack surface. This guide covers the major supply chain attack classes, how to test for exposure, and what detection looks like in practice.
The Supply Chain Threat Model
Software supply chain attacks succeed because of a fundamental trust assumption: code imported from a package registry or internal repository is assumed to be what it claims to be. When that assumption breaks — because a package name is claimed by an attacker, or a legitimate package is compromised — the malicious code runs with the same trust as first-party code.
The attack classes divide roughly into:
- Dependency confusion — exploiting how package managers resolve private vs public packages
- Typosquatting — publishing a near-identical package name to intercept mistaken installs
- Namespace hijacking — claiming a package namespace that was previously abandoned or transferred
- Compromised maintainer — taking over a legitimate package by compromising its owner's credentials
- Malicious version injection — publishing a malicious release to an existing legitimate package
- Build pipeline compromise — injecting malicious code into CI/CD before the build artifact is produced
1. Dependency Confusion Attacks
Dependency confusion was systematically demonstrated by Alex Birsan in 2021, when he received bug bounty payouts from over 35 major companies including Apple, Microsoft, and PayPal — all by registering public package names that matched internal private packages used at those companies.
The mechanics: package managers like npm, pip, and RubyGems will, by default, prefer packages from the public registry over an internal registry when the same name exists in both. If a company uses an internal npm package called @company/utils but an attacker registers company-utils (or even @company/utils if the scoped namespace is unregistered) in the public registry at a higher version number, an npm install may pull the attacker's package instead.
Testing exposure to dependency confusion
To test whether an application is vulnerable:
- Identify internal package names — check
package.json,requirements.txt,Gemfile,go.mod, or equivalent manifest files. Internal packages are often identifiable by custom scopes, internal domain references, or names not present in the public registry. - Check whether the same name exists in the public registry at a higher version than the internal version.
- Check whether the package manager configuration enforces internal-only resolution for private packages.
# Check if a package name is claimed in the public npm registry
npm info @company/internal-package
# If it returns "npm ERR! 404 Not Found" — the namespace is unclaimed
# That's exploitable via dependency confusion
# Check pip for Python packages
pip index versions internal-package-name 2>/dev/null || echo "Not found in public PyPI"
# Check RubyGems
gem search internal-gem-name
Mitigations
The primary mitigations for dependency confusion are:
- Claim the public namespace — register your internal package names in the public registry (even as empty/stub packages) to prevent an attacker from claiming them
- Scoped packages with enforced resolution — use scoped npm packages (
@yourcompany/) and configurenpmrcto always resolve that scope from your internal registry - Hash pinning — lock dependency trees to specific content hashes (lockfiles), not just version ranges
- Private registry with allowlist — use a dependency proxy that only permits known-good packages
# .npmrc configuration to enforce internal registry for a scope
@yourcompany:registry=https://registry.internal.yourcompany.com/
always-auth=true
2. Typosquatting
Typosquatting exploits the gap between what a developer intends to install and what they actually type. A package named lodahs (transposed 's' and 'h' in lodash) or crossenv (a real incident — not cross-env) can intercept installs from developers who make a typo.
The attack is simple: publish a malicious package with a near-identical name to a popular one. The payload executes during installation (via preinstall or postinstall npm scripts, or setup.py in Python) and exfiltrates environment variables, credentials, or SSH keys.
Testing for typosquatting exposure
Testing for typosquatting at an organizational level means:
- Auditing your dependency manifests for unusual or unfamiliar package names
- Verifying that packages are from the expected publisher (check the maintainer, GitHub repo, and download count)
- Checking for common typosquat variants of your popular dependencies
# Generate common typosquats of a package name and check if they exist
# Example variations for "express":
# - expresss (extra character)
# - expres (missing character)
# - exprees (substituted character)
# - exporess (transposed characters)
for pkg in expresss expres exprees exporess; do
result=$(npm info "$pkg" 2>&1)
if echo "$result" | grep -q "latest"; then
echo "POTENTIAL TYPOSQUAT: $pkg exists in registry"
fi
done
Tools like depscan and confused (a Go tool by Visma R&D) automate typosquat detection across package manifests. The Socket.dev platform monitors npm and PyPI for packages that exhibit supply chain attack patterns.
3. Software Composition Analysis (SCA)
SCA is the automated process of identifying and analyzing open-source components in your codebase — their versions, known CVEs, licenses, and provenance. It's the baseline control for supply chain security.
What SCA scans look for
- Known CVEs — dependencies with documented vulnerabilities in the NVD, GitHub Advisory Database, or OSV
- Outdated versions — components significantly behind current release, missing security patches
- Deprecated packages — unmaintained packages that no longer receive security fixes
- License violations — copyleft licenses (GPL) that may conflict with proprietary usage
- Transitive dependencies — vulnerabilities in dependencies-of-dependencies, not just direct imports
SCA tools
# npm audit (built-in, checks against npm advisory database)
npm audit
npm audit fix # auto-fix where possible
npm audit --json > audit-report.json
# pip-audit (Python)
pip install pip-audit
pip-audit
pip-audit --output-format json > audit-report.json
# OWASP Dependency-Check (polyglot, Java-focused)
dependency-check --project "MyApp" --scan ./path/to/app --out ./reports
# Trivy (container and filesystem scanning)
trivy fs --security-checks vuln ./
trivy image myapp:latest
# Syft (SBOM generation) + Grype (vulnerability scanning)
syft . -o spdx-json > sbom.spdx.json
grype sbom:sbom.spdx.json
Generating and using SBOMs
A Software Bill of Materials (SBOM) is a machine-readable inventory of all components in a software artifact. SBOM generation is increasingly required by regulation (the US Executive Order on Cybersecurity mandates SBOMs for federal software procurement) and is becoming standard practice for commercial software distribution.
Two primary formats: SPDX (Linux Foundation) and CycloneDX (OWASP). Both are machine-readable and can be scanned against vulnerability databases.
# Generate CycloneDX SBOM from Node.js project
npx @cyclonedx/cyclonedx-npm --output-file sbom.json
# Generate CycloneDX SBOM from Python project
pip install cyclonedx-bom
cyclonedx-py environment -o sbom.json
# Scan SBOM against OSV database
grype sbom:sbom.json --output table
4. Compromised Maintainer Accounts
Even a legitimate, well-maintained package becomes a supply chain attack vector if its maintainer's credentials are compromised. The event-stream incident (2018) demonstrated this: a malicious actor gained maintainer access to a popular npm package and added a dependency that stole Bitcoin wallet credentials.
Signs of a potentially compromised package version:
- A new release that significantly expands the dependency graph
- A version that adds network calls not present in prior versions
- A release from an unfamiliar maintainer account (check publishing history)
- A version with anomalously high download velocity shortly after publication
- Install scripts (
preinstall,postinstall) added in a new release when they didn't exist before
Detecting malicious package behavior
# Check package install scripts before installing (npm)
npm pack package-name --dry-run 2>/dev/null
# Or inspect the tarball manually
npm pack package-name
tar -tzf package-name-*.tgz | head -20
# Check if a package has preinstall/postinstall scripts
npm info package-name scripts
# Audit a specific package for suspicious patterns with Socket
npx @socketsecurity/cli scan npm package-name
# Compare two versions of a package for changes
npm diff package-name@1.0.0 package-name@1.0.1
The Socket Security npm package analysis tool specifically looks for packages that: make outbound network requests in install scripts, access environment variables during install, read SSH key files, or spawn child processes during installation — all patterns consistent with supply chain attacks.
5. Build Pipeline Security
Even clean source code can be compromised if the build pipeline is not secured. Supply chain attacks increasingly target CI/CD infrastructure:
- Poisoned pipeline execution (PPE) — injecting malicious code into a build by exploiting how the CI/CD system pulls changes from feature branches or forks
- Exposed secrets in CI environments — exfiltrating AWS credentials, signing keys, or registry tokens from CI environment variables
- Compromised GitHub Actions — using a pinned SHA for third-party actions, not a mutable tag
- Artifact tampering — modifying build outputs between build and deployment
Testing CI/CD supply chain security
# Check GitHub Actions for mutable tag references (should use SHA pins)
# Bad: uses: actions/checkout@v3
# Good: uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
# Scan GitHub Actions workflow files for risky patterns
grep -r "uses:" .github/workflows/ | grep -v "@[0-9a-f]\{40\}" # Find non-SHA-pinned actions
# Check for secret exposure in workflow logs
# Look for secrets printed in run steps, echoed to output, or logged in error messages
# Audit npm publish tokens — ensure only needed CI tokens exist
npm token list
# Review what can trigger CI with write access to secrets
# Pull request workflows from forks should NOT have access to repository secrets
The StepSecurity Harden-Runner GitHub Action monitors the network and file system during CI runs, detecting outbound connections to unexpected destinations — a key indicator of a compromised dependency executing during build.
6. Provenance and Signing
Software signing allows consumers to verify that a package was produced by its claimed publisher and hasn't been tampered with in transit.
npm provenance
npm supports provenance attestations (introduced in 2023) that link a published package to the exact commit and CI workflow that produced it. Provenance is verifiable from the npm registry and provides a cryptographically signed chain from source code to published package.
# Verify package provenance
npm audit signatures package-name
# Publish with provenance (from GitHub Actions with OIDC token)
npm publish --provenance
Sigstore and cosign
The Sigstore project provides transparent, non-key-based signing for software artifacts. cosign can sign and verify container images, binaries, and SBOMs using keyless signing tied to OIDC identities.
# Verify a container image signature
cosign verify ghcr.io/organization/image:tag \
--certificate-identity="https://github.com/org/repo/.github/workflows/publish.yml@refs/heads/main" \
--certificate-oidc-issuer="https://token.actions.githubusercontent.com"
Supply Chain Security Testing Checklist
- Run SCA against all dependency manifests — npm audit, pip-audit, or equivalent
- Check for internal package names not claimed in public registries (dependency confusion exposure)
- Verify lockfiles are present and committed for all projects
- Check for packages with preinstall/postinstall scripts making network calls
- Review CI/CD workflow files for unpinned third-party action references
- Confirm CI secrets are not accessible to pull request workflows from forks
- Audit crt.sh or CT logs for unexpected certificates issued against your domains
- Generate SBOMs for production artifacts and store them for incident response
- Check abandoned or deprecated packages still present in the dependency tree
- Verify container base images are from trusted registries and are up to date
Ironimo scans web application endpoints for indicators of supply chain compromise — unexpected scripts loaded from third-party origins, misconfigured CSP that permits arbitrary script loading, and exposed build configuration files that reveal dependency structure. Pair it with your SCA tooling for comprehensive coverage.
Start free scan