Skip to content

Add dependency malware checker hook for Claude Code#1184

Open
John-David Dalton (jdalton) wants to merge 6 commits intomainfrom
feat/check-new-deps-hook
Open

Add dependency malware checker hook for Claude Code#1184
John-David Dalton (jdalton) wants to merge 6 commits intomainfrom
feat/check-new-deps-hook

Conversation

@jdalton
Copy link
Copy Markdown
Contributor

@jdalton John-David Dalton (jdalton) commented Apr 10, 2026

What this adds

A Claude Code pre-tool hook that automatically checks new dependencies for malware before they're added to the project. Runs on every Edit/Write to dependency manifest files.

How it works

  1. Detects 17+ ecosystem file types (package.json, requirements.txt, Cargo.toml, go.mod, etc.)
  2. Extracts newly-added deps (diff-aware — only checks what's new)
  3. Calls Socket.dev's malware API via SDK v4 checkMalware()
  4. Blocks the edit (exit 2) if malware found, warns on low scores

Files

  • .claude/hooks/check-new-deps/ — the hook + tests (82 pass) + README
  • .claude/settings.json — registers the hook on PreToolUse Edit|Write
  • .gitignore — tracks .claude/hooks/ and .claude/settings.json

Depends on

PR #1183 (SDK v4 bump) for checkMalware() API


Note

Medium Risk
Introduces a new Claude Code PreToolUse hook that can block Edit/Write operations based on live Socket.dev API responses, which may impact developer workflows and depends on network availability/token configuration.

Overview
Adds a new Claude Code pre-tool hook (.claude/hooks/check-new-deps) that detects newly introduced dependencies in common manifest/lock files (and GitHub Actions workflows), converts them to PURLs, and checks them via Socket.dev (sdk.checkMalware).

The hook is diff-aware (only scans deps added vs old_string), caches API results in-process, warns on low scores, and blocks the tool call (exit code 2) when malware or critical alerts are detected; .claude/settings.json registers it for Edit|Write, and .gitignore is updated to track the hook + settings. Includes a comprehensive Node test suite for extractors, diffing, caching, and end-to-end blocking behavior.

Reviewed by Cursor Bugbot for commit 096ce9f. Configure here.

Intercepts Edit/Write on dependency files across 17+ ecosystems
(npm, PyPI, Cargo, Go, Maven, etc.) and checks new deps against
Socket.dev's malware API before they're added. Uses SDK v4
checkMalware() with batch chunking, namespace-aware matching,
and in-memory caching.
@jdalton John-David Dalton (jdalton) force-pushed the feat/check-new-deps-hook branch 3 times, most recently from f1e6e29 to 14f1337 Compare April 10, 2026 03:38
Pipfile.lock is JSON (with "default" and "develop" sections keyed by
package name), not requirements.txt format. The regex-based extractPypi
silently matched zero dependencies. Add a dedicated extractPipfileLock
that parses the JSON structure correctly.
@jdalton
Copy link
Copy Markdown
Contributor Author

Cursor (@cursor) review

Empty string is a valid value for new_string (Edit that deletes content)
and old_string. Using || instead of ?? caused falsy empty strings to
fall through to the wrong field.
@jdalton
Copy link
Copy Markdown
Contributor Author

Cursor (@cursor) review

- Swift package URLs commonly end with .git (e.g. vapor.git); strip the
  suffix so the PURL lookup finds the correct package
- Remove 'brew' extractor key that matched any path ending in 'brew';
  only 'Brewfile' is the correct Homebrew manifest filename
@jdalton
Copy link
Copy Markdown
Contributor Author

Cursor (@cursor) review

1 similar comment
@jdalton
Copy link
Copy Markdown
Contributor Author

Cursor (@cursor) review

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 096ce9f. Configure here.

@jdalton
Copy link
Copy Markdown
Contributor Author

Cursor (@cursor) review

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

Bugbot Autofix is ON. A cloud agent has been kicked off to fix the reported issues.

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 096ce9f. Configure here.

}
}
return deps
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

npm extractor matches package.json metadata fields as dependencies

Medium Severity

The extractNpm function matches any "key": "value" pair where the value looks like a version specifier and the key starts with a lowercase letter. This means package.json metadata fields like "version": "1.0.0" get extracted as dependencies (here, a dep named version). The version regex ^\d matches the value 1.0.0, and the key version passes the lowercase check. During a Write (new file), these false-positive deps get sent to the malware API. If any real npm package sharing a metadata field name (e.g., version, name) were ever flagged, it would incorrectly block the edit.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 096ce9f. Configure here.

// Rust: serde = "1.0" or serde = { version = "1.0", features = [...] }
/^(\w[\w-]*)\s*=\s*(?:\{[^}]*version\s*=\s*"[^"]*"|\s*"[^"]*")/gm,
(m): Dep => ({ type: 'cargo', name: m[1] })
),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cargo.toml extractor matches all key-value metadata lines

Medium Severity

The Cargo.toml extractor regex /^(\w[\w-]*)\s*=\s*(?:\{[^}]*version\s*=\s*"[^"]*"|\s*"[^"]*")/gm matches any key = "value" line, not just those under [dependencies]. Metadata lines like name = "my-project", version = "0.1.0", and edition = "2021" are all extracted as cargo dependencies. On Write (new file creation) these all get sent to the malware API, and if any such cargo package name were flagged, a legitimate Cargo.toml creation would be incorrectly blocked.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 096ce9f. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants