detecting npm chalk/debug compromise: automated scanner for september 2025 supply chain attack
on this page
| Quick scan (pipx): | pipx run npm-vuln-scanner --help | 
| Quick scan (uvx): | uvx npm-vuln-scanner --help | 
| Install: | pip install npm-vuln-scanner | 
| Packages affected: | 26 packages (~2.5B downloads/week), only 10 fixed | 
| Critical unfixed: | debug 4.4.2, color-convert 3.1.1, duckdb 1.3.3, 13 others | 
| Scanner: | |
| JSON output: | npm-vuln-scanner --json scan | jq '.summary' | 
summary
On September 8-9, 2025, attackers compromised 26 npm packages totaling ~2.5 billion weekly downloads through the npmjs.help phishing campaign. The attack affected packages from multiple maintainers including DuckDB (CVE-2025-59037), suggesting broader account compromise beyond just the chalk organization. Malicious code intercepted cryptocurrency transactions in browser environments.
Critical: Only 10 of 26 packages (chalk-maintained) have clean versions available. 16 packages remain without fixes including critical packages like debug, DuckDB packages, and others.
Automated scanner v1.2.0 available: pip install npm-vuln-scanner or pipx run npm-vuln-scanner scan - Now detects all 26 compromised packages
timeline
- September 5, 2025: Phishing domain npmjs.helpregistered
- September 8, 2025 13:00 UTC: Malicious versions published to 22 packages across multiple maintainers
- September 8, 2025 ~15:00 UTC: Compromise discovered, packages removed (~2 hour exposure window)
- September 8, 2025 16:58 UTC: proto-tinker-wc package compromised
- September 8, 2025: Only chalk organization (10 packages) republished clean .2 versions
- September 9, 2025 01:11 UTC: DuckDB packages compromised (CVE-2025-59037) - 4 packages affected
- September 9, 2025: DuckDB packages fixed with new versions (1.3.4, 1.30.0)
- Ongoing: 16 non-chalk packages remain without clean replacement versions
affected packages
Confirmed Compromised Packages (chalk organization)
| Package | Compromised Version | Clean Version | Weekly Downloads | 
|---|---|---|---|
| chalk | 5.6.1 | ✅ 5.6.2 | ~300M | 
| ansi-styles | 6.2.2 | ✅ 6.2.3 | ~371M | 
| strip-ansi | 7.1.1 | ✅ 7.1.2 | ~261M | 
| ansi-regex | 6.2.1 | ✅ 6.2.2 | ~244M | 
| wrap-ansi | 9.0.1 | ✅ 9.0.2 | ~198M | 
| supports-color | 10.2.1 | ✅ 10.2.2 | ~287M | 
| slice-ansi | 7.1.1 | ✅ 7.1.2 | ~60M | 
| has-ansi | 6.0.1 | ✅ 6.0.2 | ~12M | 
| supports-hyperlinks | 4.1.1 | ✅ 4.1.2 | ~19M | 
| chalk-template | 1.1.1 | ✅ 1.1.2 | ~4M | 
DuckDB Packages Compromised (September 9, 2025 - CVE-2025-59037)
| Package | Compromised Version | Fixed Version | Weekly Downloads | 
|---|---|---|---|
| duckdb | 1.3.3 | ✅ 1.3.4 | ~149K | 
| @duckdb/node-api | 1.3.3 | ✅ 1.3.4-alpha.27 | ~83K | 
| @duckdb/node-bindings | 1.3.3 | ✅ 1.3.4-alpha.27 | ~72K | 
| @duckdb/duckdb-wasm | 1.29.2 | ✅ 1.30.0 | ~65K | 
Other Compromised Packages WITHOUT Clean Versions Available
| Package | Compromised Version | Maintainer | Weekly Downloads | 
|---|---|---|---|
| debug | 4.4.2 | debug-js org | ~358M | 
| color-convert | 3.1.1 | Qix- | ~194M | 
| color-name | 2.0.1 | Qix- | ~192M | 
| is-arrayish | 0.3.3 | jonschlinkert | ~74M | 
| error-ex | 1.3.3 | Qix- | ~47M | 
| color-string | 2.1.1 | Qix- | ~27M | 
| simple-swizzle | 0.2.3 | Qix- | ~26M | 
| color | 5.0.1 | unknown | ~5M | 
| proto-tinker-wc | 0.1.87 | unknown | ~1K | 
| @coveops/abi | 2.0.1 | unknown | ~500 | 
| backslash | 0.2.1 | unknown | ~0.3M | 
⚠️ CRITICAL: These 11 packages (~925M weekly downloads) were compromised but have NO clean replacement versions published. You must downgrade to earlier versions or remove these packages.
attack method
Attackers registered support@npmjs.help to mimic legitimate npm support. Initial reports indicated phishing of Sindre Sorhus (chalk maintainer), but the compromise of 26 packages across multiple maintainers (including DuckDB on September 9) suggests either:
- Multiple maintainer accounts were compromised through ongoing phishing
- Shared maintainer access was exploited
- Coordinated campaign targeting high-value packages
The fact that only chalk packages and DuckDB received fixes while 16 packages from other maintainers remain unfixed indicates the attack scope exceeded initial assessments. The DuckDB compromise (CVE-2025-59037) occurred a day after the initial attack was discovered, showing the campaign’s persistence.
malware behavior
The injected code (full malicious snippet) operates only in browser environments. The heavily obfuscated JavaScript:
- Intercepts wallet APIs: Hooks into Ethereum wallet provider methods
- Modifies transactions: Silently changes recipient addresses before execution
- Multi-chain targeting: Supports ETH, BTC, SOL, LTC, BCH
- Obfuscation techniques: Encoded arrays, symbol-based replacements, nested conditionals
- Address replacement: Swaps legitimate addresses with attacker-controlled ones mid-transaction
The code specifically targets cryptocurrency transactions, making it invisible to users until funds are stolen.
detection patterns
Search for these obfuscated signatures in your codebase:
// Function names and strings
'_0x112fa8';
'stealthProxyControl';
'runmask';
'newdlocal';
// ERC20 approve function selector
'0x095ea7b3';Primary attacker ETH address: 0xFc4a4858bafef54D1b1d7697bfb5c52F4c166976
automated scanner
npm-vuln-scanner
A purpose-built Python scanner detects these compromised packages with intelligent severity analysis:
# Install from PyPI
pip install npm-vuln-scanner
# Quick scan with pipx (recommended)
pipx run npm-vuln-scanner scan
# Or use uvx
uvx npm-vuln-scanner scanKey features (v1.2.0):
- 26 packages detected: including DuckDB packages (CVE-2025-59037) and all npmjs.help campaign packages
- zero dependencies: uses only Python standard library
- severity levels: critical (confirmed compromised), high (could install compromised), medium (safe version), warning (version unknown)
- semver analysis: understands ^,~,>=version ranges
- parallel scanning: efficient multi-threaded directory traversal
- comprehensive detection: package.json, node_modules, lock files
- json output mode: structured JSON for CI/CD integration with --jsonflag
- export formats: JSON and CSV output for automation
Example output:
🚨 CRITICAL: 2 confirmed compromised version(s) found!
⚠️  HIGH: 1 package(s) could install compromised versions
  debug
    Version: 4.4.2
    CONFIRMED: Compromised version installed
  chalk
    Version spec: ^5.6.0
    Caret range can resolve to 5.6.1Source: github.com/mjbommar/check-npm-debug-chalk | PyPI
manual detection
Check your project for compromised packages:
# Search for malicious patterns
rg -u --max-columns=80 "_0x112fa8"
rg -u "stealthProxyControl|runmask|newdlocal"
# Check installed versions
npm ls chalk debug ansi-styles color-convert strip-ansi wrap-ansi
# Verify package.json and lock files
grep -E "chalk|debug|ansi-styles|color-convert" package*.jsonReview package-lock.json or yarn.lock for specific version numbers. Monitor wallet transactions for unauthorized activity.
remediation
Update affected packages immediately:
npm update chalk debug ansi-styles color-convert strip-ansi ansi-regex wrap-ansi supports-color duckdbVerify clean versions:
- 10 chalk packages: Update to .2 versions (e.g., chalk 5.6.2, ansi-styles 6.2.3)
- 4 DuckDB packages: Update to fixed versions (duckdb 1.3.4, @duckdb/duckdb-wasm 1.30.0)
- 11 packages WITHOUT fixes - must downgrade: - debug: npm install debug@4.4.1or earlier
- color-convert: npm install color-convert@3.1.0or earlier
- color-name: npm install color-name@2.0.0or earlier
- color: npm install color@4.2.3or earlier
- is-arrayish: npm install is-arrayish@0.3.2or earlier
- error-ex: npm install error-ex@1.3.2or earlier
- color-string: npm install color-string@2.1.0or earlier
- simple-swizzle: npm install simple-swizzle@0.2.2or earlier
- proto-tinker-wc: npm install proto-tinker-wc@0.1.86or earlier
- @coveops/abi: npm install @coveops/abi@2.0.0or earlier
- backslash: npm install backslash@0.2.0or earlier
 
- debug: 
Clear cache and regenerate lock files:
npm cache clean --force
rm package-lock.json
npm installReview recent cryptocurrency transactions. Rotate exposed credentials. Enable 2FA on npm accounts.
scanner implementation details
The npm-vuln-scanner tool implements sophisticated detection through:
semver range analysis
The scanner understands npm’s semver ranges to detect potential vulnerabilities:
# Caret ranges (^5.6.0 can resolve to 5.6.1 or 5.6.2)
"^5.6.0" → HIGH severity (could install 5.6.1)
# Exact versions
"4.4.2" → CRITICAL severity (exact match)
# Tilde ranges (~7.1.0 allows patch updates)
"~7.1.0" → HIGH severity (could install 7.1.1 or 7.1.2)
# Safe versions
"5.5.0" → MEDIUM severity (package present but safe)multi-source detection
The scanner checks:
- package.json: direct, dev, and optional dependencies
- node_modules: actual installed versions with package.json parsing
- package-lock.json: lockfile v1, v2, and v3 formats
- yarn.lock: yarn-specific lockfile parsing
parallel processing
Uses Python’s ThreadPoolExecutor for concurrent:
- Directory traversal (finds all package.json files)
- node_modules scanning (checks installed versions)
- Lock file parsing (processes multiple formats)
usage patterns
# Scan multiple projects
npm-vuln-scanner scan /project1 /project2 /project3
# Include home and global npm
npm-vuln-scanner scan --include-home --include-global
# Export for CI/CD integration
npm-vuln-scanner scan --export-json report.json
# Check specific package.json
npm-vuln-scanner check ./frontend/package.json
# List all monitored packages
npm-vuln-scanner listjson output mode (v1.2.0)
Structured JSON output for CI/CD pipelines and automation:
# All commands support --json flag
npm-vuln-scanner --json scan
npm-vuln-scanner --json check package.json
npm-vuln-scanner --json list
# Parse with jq
npm-vuln-scanner --json scan | jq '.summary.by_severity.critical'
npm-vuln-scanner --json scan | jq '.findings.high[] | select(.package_name == "chalk")'
npm-vuln-scanner --json scan | jq -r '.findings.critical[] | "\(.package_name)@\(.version_spec)"'JSON structure includes:
- scan_metadata: version, timestamp, paths, duration
- summary: severity counts, exit code, packages checked
- findings: organized by severity level
- recommendations: risk-appropriate actions (correctly advises “DO NOT run npm install until specs are fixed”)
Example output:
{
  "scan_metadata": {
    "scanner_version": "1.2.0",
    "scan_timestamp": "2025-09-09T...",
    "duration_seconds": 1.31
  },
  "summary": {
    "by_severity": {
      "critical": 0,
      "high": 2,
      "medium": 257
    },
    "exit_code": 1
  },
  "findings": {
    "high": [
      {
        "package_name": "chalk",
        "version_spec": "^5.6.0",
        "message": "Caret range can resolve to 5.6.1"
      }
    ]
  }
}exit codes
- 0: no compromised packages found
- 1: compromised packages detected or error
- 130: scan interrupted by user (Ctrl+C)
context
This attack reveals critical npm ecosystem vulnerabilities:
- Incomplete remediation: 16 of 26 compromised packages (~925M weekly downloads) remain without clean versions
- Multiple maintainer compromise: Attack crossed organizational boundaries, compromising DuckDB on September 9 (CVE-2025-59037), suggesting coordinated campaign
- Response gaps: Only chalk organization and DuckDB responded quickly; other maintainers haven’t published fixes
- Ecosystem dependencies: Packages like debug,color-convert, and DuckDB are deep dependencies for thousands of projects
- Continued attacks: The campaign continued into September 9 with DuckDB packages, showing persistence beyond initial discovery
The 2-hour initial exposure window combined with ~2.5B weekly downloads means millions of systems potentially installed compromised versions. The lack of fixes for 16 packages creates ongoing risk - users must manually downgrade or remain vulnerable.