
safer-dependencies
safer-dependencies is a security layer for Claude Code that audits packages before they’re added to your project. It detects and fixes risky dependencies, in…
Install with your AI
Paste into Claude Code, Cursor, or any agent — it reads the repo and wires the tool into your project.
Install and set up safer-dependencies (release-binary project) into my current project. Found on https://claudeers.com/safer-dependencies Repo: https://github.com/robert-auger/safer-dependencies Homepage/docs: — Detected install method: release-binary → inspect the README Category: skills. Platforms: cli, api, web, mobile. Read the repo's README for exact setup and env vars, then install it and wire it into my project. Claudeers Health Verdict: active; community-verified: false. Confirm the source before running anything.
Grab the latest release asset from GitHub.
# download a build from https://github.com/robert-auger/safer-dependencies/releases
git clone https://github.com/robert-auger/safer-dependencies
// compatibility
| Platforms | cli, api, web, mobile |
|---|---|
| Operating systems | — |
| AI compatibility | claude |
| License | NOASSERTION |
| Pricing | open-source |
| Language | Python |
Safer Dependencies for Claude Code
Automatically checks dependencies that Claude adds through its Write, Edit, and Bash tools, and auto-corrects vulnerable versions in place. Runs provenance, version age, vulnerability, and hash-integrity checks across npm, PyPI, RubyGems, Maven, Go, Rust, and PHP (Composer). Coverage is scoped to writes that go through Claude's tools (Intercept Mode corrects a vulnerable pin after the file lands, within the same tool cycle — not before); see CAPABILITIES.md for exactly what is and isn't covered.
New here? GETTING-STARTED.md takes you from zero to a working install in about five minutes.
Security & privacy: see SECURITY.md (vulnerability disclosure), PRIVACY.md (data egress, no telemetry), and CAPABILITIES.md (what the tool defends against and what it doesn't).
License (source-available — NOT OSI "open source"): Free to use and modify for your own purposes, including for-profit/company internal use and building products you sell. A separate paid license is required only to monetize the software itself — selling it, shipping it inside a product or service that is sold, or offering its functionality to third parties for a fee (including hosted/SaaS/API). Redistribution and derivatives must keep the license and credit this project. See LICENSE (Section 4 for the commercial restriction); commercial-license requests via github.com/robert-auger.
Contents
- Getting started — zero to installed in about five minutes
- What it does
- How it works
- What triggers it
- What's in this repo
- Supported ecosystems
- Install
- Warning levels
- Audit log
- Requirements
- FAQ
What it does
When AI coding assistants like Claude add packages to your project, they often pick whatever version sounds right — without checking whether it has known security vulnerabilities, whether the package is still actively maintained, or whether the name is a typo away from a malicious lookalike. safer-dependencies fixes that by sitting between Claude and your manifest files and running security checks automatically, before any unsafe version lands in your code.
When Claude is about to add a package to your project, this skill intercepts and runs 5 checks:
- Provenance -- official registry, typosquat detection (npm/PyPI/RubyGems/Maven/crates.io), package age
- Version age -- picks the newest stable version published 7+ days ago (cooldown window)
- Vulnerability scan -- OSV API, with ecosystem-native tools (npm audit, pip-audit, bundle audit) when available
- Hash-pin integrity -- for PyPI
requirements.txtlines with--hash=sha256:...pins, the declared hash is validated against PyPI's published hashes; mismatch emits a WARNING - Abandoned & stale packages -- known-abandoned packages (e.g.
paperclip,request,pycrypto,github.com/dgrijalva/jwt-go) are hard-blocked immediately with a suggested replacement; packages with no stable release in 2+ years get an advisorySTALE:warning. Hard-blocked packages are removed from the manifest and Claude will ask how to proceed; stale-only packages are left in place.
If issues are found, Claude emits warnings and may step back to a safer version. All checks are logged to ~/.claude/safer-dependencies-audit-YYYY-MM.log (one file per calendar month).
How it works
The skill operates in five modes (summarized below; the deepest design rationale lives in skills/safer-dependencies.md):
Normal Mode (Manual)
When Claude is about to write an import, add a package to a manifest, or update a lock file, the skill runs inline in your session:
- Queries the package registry for stable versions
- Auto-selects the newest version published 7+ days ago (deterministic -- no LLM judgment)
- Checks for known vulnerabilities via ecosystem tools and the OSV API
- Verifies package signatures where available
- Emits warnings if issues are found, pins the exact version
- Logs the result to the audit trail
The version selection is handled by standalone Python scripts bundled with the skill, not by the LLM interpreting rules. The command outputs SELECTED: <version> and Claude uses that version exactly.
Intercept Mode (Automatic)
Configure .claude/settings.json with a PostToolUse hook to enable automatic, transparent package verification:
- Claude writes a manifest file (e.g.
package.json) with the originally-requested version — the file lands on disk - The
PostToolUsehook fires immediately after the write completes and invokessafer-dependencies-shim.sh - The shim reads the file, parses declared packages, and runs all security checks (typosquat, abandoned, CVE, staleness, hash-pin)
- If corrections are needed, the shim rewrites the manifest in place with safe versions (or removes entries that have no safe version)
- The shim emits signals (
UPDATED:,BLOCKED:,WARNING:,STALE:,MAJOR-UPDATE-CONFIRM:,REFACTOR-REQUIRED:,REGRESSION:,TYPOSQUAT-CONFIRM:,VERIFY:,CLEAN:) viahookSpecificOutput.additionalContexton stdout.REGRESSION:precedes aMAJOR-UPDATE-CONFIRM:when the audit log shows the same (file, package) was previously corrected to the same safe target — that is, a subagent or stale plan has re-introduced a known-vulnerable version, and the orchestrator should restore the previously-approved version rather than re-deciding the major bump. - Claude receives those signals as a system-reminder and performs follow-up work (find affected imports, run tests, refactor for breaking changes)
Design note — Shape C (post-write corrective): the hook does NOT block writes. Each vulnerable version lands on disk first and is then auto-corrected within the same tool-use cycle. This is a deliberate choice over a PreToolUse blocking design — see FAQ.md for the tradeoffs.
Example signal:
UPDATED: aiohttp 3.8.5 → 3.9.0 (HIGH: 33 CVEs fixed)
The parent agent uses these signals to identify affected code and refactor as needed.
Pre-Install Mode (Bash Hook)
Configure .claude/settings.json with a PreToolUse:Bash hook to enable
pre-flight auditing of package-manager install commands. This complements
(does not replace) Intercept Mode — together they form a layered defense.
- Claude attempts a Bash tool call (e.g.
npm install [email protected]) - The
PreToolUsehook fires before the call runs and invokessafer-dependencies-pretooluse-bash.sh - A pure-bash early filter short-circuits non-PM commands in ~115 ms
(no Python invocation), so
git status/ls/npm testpay negligible cost on the hot path - For recognized package-manager installs (
npm/pnpm/yarninstall/i/add), the helper tokenizes viashlex, extracts eachpkg@versionargument, and POSTs to OSV - Any vulnerable concrete pin → the hook returns
permissionDecision: "deny"with a per-finding GHSA-id + CVSS + summary, plus a hint to invoke the safer-dependencies skill - The install never runs — no network fetch, no postinstall scripts
Why this exists in addition to Intercept Mode: the post-write shim
is blind to Bash. npm install [email protected] runs to completion (and
postinstall scripts execute) before any audit fires; npm install -g typosquat-pkg writes no project manifest at all. Pre-Install Mode
closes those gaps structurally.
Pre-Install Mode only sees what the user typed (pkg@version args on
the command line). It can't see the transitive tree the resolver will
actually install. Post-Install Mode (below) audits the lockfile once
the install completes — the two modes are complementary, not redundant.
Scope: the package-manager CLIs covered here span five ecosystems
(npm/pnpm/yarn/bun/npx/deno, pip/pip3/pipx/pipenv/uv/uvx/poetry, gem/bundle,
go, cargo), plus Maven via Intercept Mode (Maven dependencies are typically
declared in pom.xml/build.gradle, not added via a CLI verb).
Known gap: the Maven CLI does support direct downloads via
mvn dependency:get -Dartifact=group:art:versionandmvn dependency:copy. This hook does not yet recognize those invocations. If you use them regularly, the existing post-write shim still catches whatever lands in your manifest, but the pre-fetch protection only applies to the ecosystems listed above. Tracked as a follow-up.
Per-ecosystem syntax recognized:
| PM | Verbs | Concrete-pin syntax |
|---|---|---|
npm, pnpm, yarn, bun | install, i, add (plus yarn/pnpm dlx, bun x, yarn create) | [email protected], @scope/[email protected] |
npx | (verbless — package is first positional) | [email protected] |
deno | add, install | npm:[email protected] (npm-prefixed specs) |
pip, pip3, pipx, pipenv, uv, uvx, poetry | install (pip/pip3/pipx/pipenv) / add (uv/poetry) / verbless (uvx) | pkg==1.2.3 (extras pkg[extra]==X also handled) |
gem, bundle | install (gem) / add | -v 1.2.3, --version 1.2.3, --version=1.2.3 (separate flag) |
go | get, install | [email protected] (must include v prefix per Go modules) |
cargo | add, install | [email protected] |
Range pins (npm ^4.17, pip >=, poetry ^/~, Go @latest) and
unspecified versions pass through to Intercept Mode after install — the
post-write shim audits whatever the resolver picks. Auto-rewrite to a
safe version is queued as a follow-up.
Failure mode: fail-open. Any error (Python missing, network blip, malformed input) exits 0 with no output, allowing bash to proceed. Intercept Mode still runs after install, so a failed pre-flight degrades gracefully to existing protection.
Example deny:
safer-dependencies pre-flight audit blocked this install.
Vulnerable pinned version(s) detected:
- [email protected] → GHSA-35jh-r3h4-6jhm (CVSS:7.4): Command Injection in lodash
Re-run with a patched version, or invoke the safer-dependencies skill
for a recommended pin.
Post-Install Mode (Bash Hook)
Configure .claude/settings.json with a PostToolUse:Bash hook to enable
post-flight auditing after Bash commands. It runs three independent scans
against the command's cwd, each closing a gap the other hooks can't address:
- Scan A — lockfiles. After a successful install verb (
npm install,bundle install,poetry install,uv sync,go mod tidy, etc.), audits freshly-modified lockfiles (package-lock.json,Gemfile.lock,poetry.lock,uv.lock,go.sum,yarn.lock,pnpm-lock.yaml,Pipfile.lock). This closes the transitive-CVE gap Pre-Install can't see: the user typedpkg@version, but the resolver may have pulled in dozens of transitives no one named. - Scan B — manifests. After any Bash command not on a read-only
denylist (
ls,cat,git status, …), audits freshly-modified manifests. This is the only fallback for manifest edits made viased -i,jq, or a script — those bypass theWrite/Edittool that Intercept Mode hooks on. - Scan C — resolved environment. Plain
pip install/pip install -r requirements.txtwrites no lockfile, so Scan A never sees the resolved tree. After a pip-shaped install, Scan C re-invokes the same pip with a read-onlylist --format=jsonand OSV-checks the full resolved environment (direct + transitive).
How a scan runs:
- Claude runs a Bash tool call
- The
PostToolUsehook fires after the command completes and invokessafer-dependencies-posttooluse-bash.sh - A pure-bash early filter short-circuits commands that match no scan gate in
~115 ms (same fast-path convention as Pre-Install), so
ls/git/catpay negligible cost - Each scan walks
cwdwithfind -maxdepth 5(covers monorepo layouts; excludesnode_modules,.git,.venv,venv) for files modified within the last 60 s — override viaSAFE_DEP_POSTINSTALL_MTIME_WINDOW - For each freshly-modified file (Scan A/B), the hook forges a synthetic
PostToolUse:Writepayload and pipes it to the existing shim — the shim's lockfile and manifest auditors run unchanged, no duplicated logic - Per-file signals are concatenated and emitted as one
hookSpecificOutputJSON to the parent agent
What it catches that Pre-Install doesn't: transitive vulnerabilities.
A clean-looking bundle install can pull [email protected] (CVE-2025-27610)
as a transitive of sinatra — the user never typed rack, so
Pre-Install can't see it, but Post-Install reads the resolved
Gemfile.lock and reports the CVE.
Scope: Scan A does not rewrite resolved versions — the auto-correct
contract only applies to manifests Claude wrote directly. For transitive CVEs,
the fix is typically "update the direct dep that owns the transitive," which
needs human judgment. Scan B does auto-correct, because it audits manifests
through the same shim path as Intercept Mode. Scan A skips when the
transitive check tier is set to off (config set checks.transitive off).
Failure mode: fail-open, same as other hooks. Any error (missing shim, malformed payload, Python unavailable) exits 0 silently.
Example WARNING:
WARNING: [email protected] in lock file has GHSA-29mw-wpgm-hmr9, GHSA-35jh-r3h4-6jhm
Post-Agent Mode (Agent Hook Pair)
The four modes above only fire for root-session tool calls. When the root
session dispatches a subagent (via the Agent tool — many skills and slash
commands do this internally), the subagent's Write/Edit/Bash calls bypass all
of them. Post-Agent Mode is the reactive safety net for that gap.
- A
PreToolUse:Agenthook (safer-dependencies-pretooluse-agent.sh) runs immediately before each Agent dispatch and touches a sentinel file at/tmp/.safer-deps-agent-<PPID>-<session_id>.sentinel(falling back to a PPID-only name when no session id is available) - The subagent runs and may write manifests or lockfiles
- A
PostToolUse:Agenthook (safer-dependencies-posttooluse-agent.sh) runs after the Agent call returns,finds every manifest and lockfile newer than the sentinel, and audits each via the same shim path - Findings surface as
additionalContextto the root session's next turn; the sentinel is removed
Nested subagents are covered automatically — the root's PostToolUse:Agent
fires only after all of the outer agent's work (including anything it
dispatched) is on disk. The one gap is a global install that writes no
manifest or lockfile (npm install -g …): there is nothing to scan. Like the
other hooks, it fails open — any error (missing sentinel, missing shim,
unreadable payload) exits 0 silently. Full design rationale lives in
skills/safer-dependencies.md.
What triggers it
The skill fires automatically when Claude:
Manifest / install operations
- Adds or updates a package in
package.json,requirements.txt,Gemfile,pom.xml,build.gradle,Cargo.toml,go.mod, or any other supported manifest - Writes an
import,require, orusefor a package not already declared in the manifest - Generates or updates a lock file (checks only new/changed entries)
- Runs a package-manager install via Bash (
npm install,bundle install,poetry install,uv sync,go mod tidy, etc.) — Pre-Install audits the command args, Post-Install audits the resulting lockfile - Writes a
Dockerfileor CI workflow (.github/workflows/*.yml, etc.) that embeds pinned package-manager install steps
Selection & recommendation questions
- Library/framework comparisons: "should I use axios or node-fetch?", "moment vs dayjs?", "which is better X or Y?"
- Recommendation requests: "what's a good HTTP client for Python?", "recommend a logging library for Go", "what package handles CSV in Node?"
- Version selection: "what version of Django should I use?", "latest stable Flask?"
Intent-to-use expressions (pre-add)
- "I want to use FastAPI for this", "I'm thinking of adding Celery", "we're looking at Prisma as the ORM", "let's use Tailwind"
Package health and trust questions
- "Is moment.js still maintained?", "is this gem still active?", "is X abandoned?", "is X EOL?", "can I trust this package?", "when was faker last updated?"
Scaffolding commands
npx create-react-app,npm create vite@latest,django-admin startproject,rails new,cargo new+cargo add, "bootstrap a new FastAPI project"
Implicit package adds (feature requests that imply a new dependency)
- "Add Redis caching to the app", "connect to Postgres", "add JWT auth", "write code to send emails" — fires when no package for that capability is already in the manifest
Migration and porting
- "Migrate from requests to httpx", "move from CRA to Vite", "port from moment to date-fns" — audits the incoming package
It does not fire for:
- Standard library imports (
os,fs,java.util.*, etc.) - Already-declared dependencies that aren't being changed
- Academic discussion of how a package works internally ("explain React's reconciler", "how does webpack's module resolution work?") — comparison and selection questions do still fire
- Installing OS-level apps, runtimes, or IDE extensions (Python itself, Docker, Homebrew, VS Code extensions)
What's in this repo
This is a skill + hook bundle, not a single skill file. A complete install deploys these pieces:
| File | Role |
|---|---|
skills/safer-dependencies.md | The skill (SKILL.md once installed). Describes audit procedures and includes management mode for installation/stats. |
skills/safer-dependencies-shim.sh | PostToolUse:Write/Edit hook — audits manifest + lockfile writes and auto-corrects vulnerable versions in place (Intercept Mode). |
skills/safer-dependencies-pretooluse-bash.sh | PreToolUse:Bash hook — pre-flight OSV audit of package-manager install commands; denies vulnerable concrete pins before the install runs (Pre-Install Mode). |
skills/safer-dependencies-posttooluse-bash.sh | PostToolUse:Bash hook — post-flight audit after Bash commands; catches transitive CVEs in freshly-written lockfiles, manifests edited via sed/jq/scripts, and the resolved environment of plain pip install (Post-Install Mode). |
skills/safer-dependencies-pretooluse-agent.sh + skills/safer-dependencies-posttooluse-agent.sh | PreToolUse:Agent + PostToolUse:Agent hook pair — closes the subagent coverage gap. Modes 2–4 only fire for root-session tool calls, so any manifest a subagent writes bypasses them. Post-Agent audits whatever the subagent wrote after each Agent tool-call returns (Post-Agent Mode). |
skills/scripts/ | Shared Python library (safedep/) and standalone resolver scripts used by all hooks. |
skills/scripts/safer_dependencies_manager.py | Management module for interactive installation, usage stats, and setup validation. |
The skill file alone is not enough — without hooks, automatic invocation depends on Claude deciding to reach for the skill. Install all five pieces for full coverage; many skills and slash commands dispatch subagents internally, so the Post-Agent pair matters even if you never explicitly spawn one. (See FAQ.md for why a skill on its own can't guarantee coverage.)
Supported ecosystems
| Ecosystem | Manifest | Lock file |
|---|---|---|
| npm | package.json | package-lock.json, yarn.lock, pnpm-lock.yaml |
| PyPI | requirements.txt, pyproject.toml, Pipfile, setup.py, setup.cfg | Pipfile.lock, poetry.lock, uv.lock |
| RubyGems | Gemfile, *.gemspec | Gemfile.lock |
| Maven | pom.xml, build.gradle, libs.versions.toml | -- |
| Go | go.mod | go.sum |
| Rust | Cargo.toml | Cargo.lock |
| PHP (Composer) | composer.json | composer.lock |
Install
New to the project? Start with GETTING-STARTED.md. The short version:
git clone https://github.com/robert-auger/safer-dependencies /tmp/safer-dependencies
python3 /tmp/safer-dependencies/skills/scripts/safer_dependencies_manager.py interactive_install
The installer prompts for scope (global vs project) and which hooks to enable, then writes settings.json for you — both the hook entries and the permissions allowlist that lets the skill's check commands run without an approval prompt on every audit.
Everything else install-related lives in INSTALLATION.md, the single reference for install mechanics: manual file-by-file installs (global and project-level), Windows specifics, Post-Agent hooks, the permissions allowlist, verifying the setup, updating, pinning to a release tag, and uninstalling.
After install, day-to-day management works via natural language to Claude — install safer-dependencies (re-run / change hooks), show safer-dependencies stats, check safer-dependencies setup — or the /safer-dependencies menu. Updating is in-session too: /safer-dependencies update applies the latest release (update --check for a dry-run, update --rollback to undo); see INSTALLATION.md for the trust model.
Platform note: macOS, Linux, and Windows are supported. Windows needs Git for Windows (provides bash) and Python 3 on
PATH— no WSL required.
Configuration
Two things are configurable after install:
- Permissions allowlist — pre-approves the skill's read-only check commands (
curl,npm view,pip-audit, …) so audits run without an approval prompt each time. The interactive installer writes the core entries for you; manual installs add the full block by hand. Full block and rationale: INSTALLATION.md → Permissions allowlist. - Security policy — the release-age cooldown window/mode and a per-check
off/warn/blocktier for every check type, edited with/safer-dependencies configand stored in~/.config/safer-dependencies/config.toml. Schema and tier semantics:skills/references/configuration.md.
Warning levels
| Level | Meaning | Example |
|---|---|---|
| CRITICAL | Stop and ask user | Typosquat detected, tampered signature |
| HIGH | Warn and proceed | Known CVE, package < 30 days old |
| MEDIUM | Warn and proceed | Version < 7 days old, missing signature |
| LOW | Warn and proceed | Unsigned Ruby gem (expected) |
Audit log
Every check is logged to ~/.claude/safer-dependencies-audit-YYYY-MM.log (one file per calendar month, where YYYY-MM is the UTC year-month) as a single JSON line. Override the full path with the SAFE_DEP_AUDIT_LOG environment variable (when set, the date suffix is not appended). Files are also size-rotated when they exceed SAFE_DEP_LOG_MAX_BYTES (default 10 MiB; set to 0 to disable). Set SAFE_DEP_MODEL to override the model value written to source.model in each entry — useful for A/B comparisons between model versions.
All five modes append to the same file. Each entry carries a source block (schema 2.2) identifying which component wrote it:
source.component | Written by | Trigger |
|---|---|---|
shim.posttooluse | shim.sh | Manifest or lockfile write (Intercept Mode, Post-Install dispatch) |
shim.install_error | shim.sh | Shim preflight install failure |
bash.pretooluse | pretooluse-bash.sh | Bash install command (Pre-Install Mode) |
bash.posttooluse | posttooluse-bash.sh | Post-Install Bash hook itself, when it fail-opens before reaching the shim |
agent.pretooluse | pretooluse-agent.sh | Reserved for Pre-Agent fail-open events (the hook itself is currently silent on success) |
agent.posttooluse | posttooluse-agent.sh | Post-Agent hook fail-open events (e.g. shim missing, python_missing) |
manual.skill | Claude running Normal Mode | Manual audit invoked inline |
source.model records the Claude Code model active in the session (e.g. "claude-sonnet-4-6"). Present in schema 2.1+; entries written by older installs omit the field. The stats command degrades gracefully to "unknown" when it is absent.
Filter by source.component with jq:
jq -r '.source.component' audit.log | sort | uniq -c | sort -rn
jq -c 'select(.source.component == "bash.pretooluse")' audit.log
# Surface every silent fail-open across all hooks:
jq -c 'select(.source.mode == "fail_open") | {component: .source.component, reason: .fail_open.reason, ts}' audit.log
For easier analysis, ask Claude for usage statistics instead of parsing logs manually:
"Show safer-dependencies stats for the last month"
This provides human-readable summaries of activity, security impact, and performance metrics extracted from these audit logs.
Entry shapes (schema 2.2). Three distinct shapes share the same ts / schema / source header:
| Shape | When it's written | Distinguishing fields |
|---|---|---|
| Audit entry | Manifest / lockfile / bash-install audit | file, ecosystem, checked, findings, abandoned, stale, typosquat, unknown, signatures, notes, clean |
| Install-error entry | Shim preflight install-error (component shim.install_error) | install_error, shim_dir, scripts_dir |
| Fail-open entry | Any hook entry-point exits early because of helper_missing / shim_missing / python_missing. source.mode is "fail_open" | fail_open: { reason, detail? } |
Audit entries: Intercept Mode runs the full pipeline (provenance, version age, OSV, abandoned/stale, typosquat, signatures), so all arrays can populate. Pre-Install Mode runs OSV only today, so abandoned / stale / typosquat / signatures are always empty. Post-Install dispatch (lockfile audit) writes under shim.posttooluse with findings populated by WARNING: strings from the lockfile auditors. The notes array carries informational NOTE: signals (e.g. manifest-skipped-because-unpinned).
Schema 2.2 added — additively — four fields to lockfile audit entries: lockfile, manifest_ref, relation_summary (a direct/transitive/unknown classification of each flagged package against the sibling manifest), and a policy block recording the transitive tier in force. The bump is backward-compatible: readers of 2.1 entries tolerate the new fields, and the source.model field remains present from 2.1 onward.
{
"ts": "2026-04-19T12:34:56Z",
"schema": "2.2",
"source": {
"component": "shim.posttooluse",
"script": "shim.sh",
"hook": "PostToolUse:Write",
"tool": "Write",
"mode": "intercept",
"model": "claude-sonnet-4-6"
},
"file": "/path/to/project/package.json",
"ecosystem": "npm",
"checked": ["[email protected]", "[email protected]"],
"findings": ["UPDATED: express 4.18.2 → 4.22.1 (HIGH: 1 CVE fixed)"],
"abandoned": [],
"stale": [],
"typosquat": [],
"unknown": [],
"signatures": [],
"notes": [],
"clean": ["[email protected]"]
}
Pre-Install Mode example (Bash hook, vulnerable pin denied):
{
"ts": "2026-04-23T06:56:21Z",
"schema": "2.2",
"source": {
"component": "bash.pretooluse",
"script": "pretooluse-bash.sh",
"hook": "PreToolUse:Bash",
"tool": "Bash",
"mode": "intercept",
"model": "claude-sonnet-4-6"
},
"file": "bash:npm install [email protected] [email protected]",
"ecosystem": "npm",
"checked": ["[email protected]", "[email protected]"],
"findings": [
"BLOCKED: [email protected] GHSA-35jh-r3h4-6jhm (CVSS:3.1/...): Command Injection in lodash"
],
"abandoned": [],
"stale": [],
"typosquat": [],
"unknown": [],
"signatures": [],
"notes": [],
"clean": ["[email protected]"]
}
Fail-open Mode example (Post-Install Bash hook called with no shim adjacent — broken install):
{
"ts": "2026-05-03T07:14:11Z",
"schema": "2.2",
"source": {
"component": "bash.posttooluse",
"script": "safer-dependencies-posttooluse-bash.sh",
"hook": "PostToolUse",
"tool": "Bash",
"mode": "fail_open",
"model": "claude-sonnet-4-6"
},
"fail_open": {
"reason": "shim_missing",
"detail": "/home/alice/.claude/skills/safer-dependencies"
}
}
A fail-open entry says: "this hook fired but exited early without auditing because something prerequisite was missing." Use the jq filter above (select(.source.mode == "fail_open")) to surface every silent loss-of-protection event in your log.
When the shim runs in dry-run mode (SAFE_DEP_DRY_RUN=1), entries also include "mode": "dry_run" so post-hoc analysis can filter audit-only invocations.
Requirements
- Python 3.9+ (the hooks probe for this and fail-open on older interpreters)
curl(for registry API calls and OSV vulnerability checks)- Ecosystem tools (optional, skill falls back to OSV API if missing):
npmfor npm packagespip-auditfor Python packagesbundlefor Ruby packagesdependency-checkfor Java packages
FAQ
Design-decision rationale (why PostToolUse instead of PreToolUse, why signatures aren't verified, why scripts and shim are duplicated, skill-loading gotchas, etc.) is documented in FAQ.md.
// faq
What is safer-dependencies?
safer-dependencies is a security layer for Claude Code that audits packages before they’re added to your project. It detects and fixes risky dependencies, including CVEs, typosquats, abandoned packages, version-age issues, and adds package-cooldown violations across npm, PyPI, RubyGems, Maven, Go, and Rust.. It is open-source on GitHub.
Is safer-dependencies free to use?
safer-dependencies is open-source under the NOASSERTION license, so it is free to use.
What category does safer-dependencies belong to?
safer-dependencies is listed under skills in the Claudeers registry of Claude-compatible tools.
// embed badge
[](https://claudeers.com/safer-dependencies)
// retro hit counter
[](https://claudeers.com/safer-dependencies)
// reviews
// guestbook
// related in Claude Skills
An agentic skills framework & software development methodology that works.
💫 Toolkit to help you get started with Spec-Driven Development
AI coding assistant skill (Claude Code, Codex, OpenCode, Cursor, Gemini CLI, and more). Turn any folder of code, SQL schemas, R scripts, shell scripts, docs,…