detecting npm chalk/debug compromise: automated scanner for september 2025 supply chain attack

published: September 8, 2025 updated: September 9, 2025

tl;dr

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:PyPI | GitHub
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.help registered
  • 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)

PackageCompromised VersionClean VersionWeekly Downloads
chalk5.6.1✅ 5.6.2~300M
ansi-styles6.2.2✅ 6.2.3~371M
strip-ansi7.1.1✅ 7.1.2~261M
ansi-regex6.2.1✅ 6.2.2~244M
wrap-ansi9.0.1✅ 9.0.2~198M
supports-color10.2.1✅ 10.2.2~287M
slice-ansi7.1.1✅ 7.1.2~60M
has-ansi6.0.1✅ 6.0.2~12M
supports-hyperlinks4.1.1✅ 4.1.2~19M
chalk-template1.1.1✅ 1.1.2~4M

DuckDB Packages Compromised (September 9, 2025 - CVE-2025-59037)

PackageCompromised VersionFixed VersionWeekly Downloads
duckdb1.3.3✅ 1.3.4~149K
@duckdb/node-api1.3.3✅ 1.3.4-alpha.27~83K
@duckdb/node-bindings1.3.3✅ 1.3.4-alpha.27~72K
@duckdb/duckdb-wasm1.29.2✅ 1.30.0~65K

Other Compromised Packages WITHOUT Clean Versions Available

PackageCompromised VersionMaintainerWeekly Downloads
debug4.4.2debug-js org~358M
color-convert3.1.1Qix-~194M
color-name2.0.1Qix-~192M
is-arrayish0.3.3jonschlinkert~74M
error-ex1.3.3Qix-~47M
color-string2.1.1Qix-~27M
simple-swizzle0.2.3Qix-~26M
color5.0.1unknown~5M
proto-tinker-wc0.1.87unknown~1K
@coveops/abi2.0.1unknown~500
backslash0.2.1unknown~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 scan

Key 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 --json flag
  • 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.1

Source: 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*.json

Review 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 duckdb

Verify 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.1 or earlier
    • color-convert: npm install color-convert@3.1.0 or earlier
    • color-name: npm install color-name@2.0.0 or earlier
    • color: npm install color@4.2.3 or earlier
    • is-arrayish: npm install is-arrayish@0.3.2 or earlier
    • error-ex: npm install error-ex@1.3.2 or earlier
    • color-string: npm install color-string@2.1.0 or earlier
    • simple-swizzle: npm install simple-swizzle@0.2.2 or earlier
    • proto-tinker-wc: npm install proto-tinker-wc@0.1.86 or earlier
    • @coveops/abi: npm install @coveops/abi@2.0.0 or earlier
    • backslash: npm install backslash@0.2.0 or earlier

Clear cache and regenerate lock files:

npm cache clean --force
rm package-lock.json
npm install

Review 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:

  1. package.json: direct, dev, and optional dependencies
  2. node_modules: actual installed versions with package.json parsing
  3. package-lock.json: lockfile v1, v2, and v3 formats
  4. 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 list

json 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:

  1. Incomplete remediation: 16 of 26 compromised packages (~925M weekly downloads) remain without clean versions
  2. Multiple maintainer compromise: Attack crossed organizational boundaries, compromising DuckDB on September 9 (CVE-2025-59037), suggesting coordinated campaign
  3. Response gaps: Only chalk organization and DuckDB responded quickly; other maintainers haven’t published fixes
  4. Ecosystem dependencies: Packages like debug, color-convert, and DuckDB are deep dependencies for thousands of projects
  5. 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.

references

on this page