plugins/elixir-phoenix/skills/deps-audit/SKILL.md
Audit Hex deps for supply-chain security risk — bidi chars, compile-time exec, maintainer changes, typosquats, CVEs. Use after mix deps.update, when checking if a package upgrade is safe, or reviewing mix.lock PR diffs.
npx skillsauth add oliver-kriska/claude-elixir-phoenix deps-auditInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Non-mutating supply-chain audit for Hex packages. Runs an 8-rule MVP catalogue
against changed packages, enriches with Hex API metadata, wraps existing tools
(mix hex.audit, mix_audit, OSV-Scanner), and emits a triage table.
mix deps.update or mix deps.get brought in new versionsmix.lock (pre-merge gate)--preview <pkg>).claude/deps-audit/last-run.json — its absence
is evidence the audit didn't actually run.mix_audit / osv-scanner — even if asked. Detect,
warn with install instructions, skip cleanly if missing. If the user
says "install it," respond with the install command (e.g.,
mix deps.add mix_audit --only dev) and do not execute it. The
audit skill is non-mutating; mix.exs / mix.lock are off-limits
regardless of consent.rule_id, severity, file:line, snippet, message. No
handwaving.(old, new) pairs
resolved.hex-deps-triager agent runs only when score
10 (1 BLOCK or 3+ WARNs), and its verdicts are advisory — never auto-suppress a finding without human review.
| Mode | Trigger | Old source | New source |
|------|---------|-----------|-----------|
| B (default) | /phx:deps-audit | git show HEAD:mix.lock | working mix.lock |
| C (PR) | /phx:deps-audit --base main | git show <ref>:mix.lock | working mix.lock |
| A (preview) | /phx:deps-audit --preview httpoison | locked version | Hex API latest |
See ${CLAUDE_SKILL_DIR}/references/operating-modes.md for full resolver logic.
Default = full 8-rule scan with streaming progress. --quick opts out
to CVE + retirement only. See ${CLAUDE_SKILL_DIR}/references/execution-flow.md.
Parse the mix.lock Erlang term format for both old and new sources. Emit a
list of {pkg, old_version, new_version} tuples. Surface
new-only and removed-only packages separately (a removed package is not
audited; a brand-new package gets old_version = nil and skips diff-only
rules).
See ${CLAUDE_SKILL_DIR}/references/diff-resolver.md for shell + mix run -e snippets per mode and the JSON output contract.
For each (pkg, old, new):
mix hex.package fetch <pkg> <old> --unpack -o ${AUDIT_TMPDIR}/tarballs/<pkg>/<old>/
mix hex.package fetch <pkg> <new> --unpack -o ${AUDIT_TMPDIR}/tarballs/<pkg>/<new>/
All ephemeral artifacts live under ${AUDIT_TMPDIR} (driver-owned, removed
on exit). See ${CLAUDE_SKILL_DIR}/references/audit-tmpdir.md and
${CLAUDE_SKILL_DIR}/references/tarball-fetcher.md.
| # | Rule | Sev | Method |
|---|------|-----|--------|
| 1 | Bidi Unicode control chars in .ex/.exs/.erl | BLOCK | grep |
| 2 | Code.eval_* / :erlang.apply with non-literal MFA at module scope | BLOCK | AST (Sourceror or regex+scope) |
| 3 | System.cmd / :os.cmd / Port.open at compile time | BLOCK | AST |
| 4 | :erlang.binary_to_term/1 on literal without :safe | BLOCK | AST |
| 5 | New :git/:path dep in mix.exs (vs old) | BLOCK | AST diff |
| 6 | Maintainer change between versions | BLOCK | Hex API |
| 7 | Base64 blobs >256 chars outside priv/static/, test/fixtures/, assets/ | WARN | regex |
| 8 | Levenshtein ≤2 from top-500 + download delta >1000× | BLOCK | Hex API + fuzzy |
Full catalogue (35 rules, MVP marked) in ${CLAUDE_SKILL_DIR}/references/heuristics.md.
Bash + mix run -e implementations for all 8 MVP rules in
${CLAUDE_SKILL_DIR}/references/rules-impl.md (single-pass NEW + diff rules +
Hex API rules, with run_all_rules master loop).
mix hex.audit — retired-package check, always availablemix_audit — CVE check via GHSA, if installed (else warn + skip; do NOT install)osv-scanner — CVE check via OSV.dev, if installed (else warn + skip; do NOT install)See ${CLAUDE_SKILL_DIR}/references/external-tools.md for detection, output parsing, and severity mapping per tool.
GET /api/packages/:name — owners, downloads, inserted_atGET /api/packages/:name/releases/:version — per-release publisherdays_since_publish, owner_age_days, download_velocityCap at 5 req/sec. Per-run cache under ${AUDIT_TMPDIR}/hex-api/.
See ${CLAUDE_SKILL_DIR}/references/hex-api.md for endpoint contracts,
caching strategy, Rule 6/8 detection, and Levenshtein implementation.
hex_vet.exs ledger (if present)If hex_vet.exs exists at project root, vetted-version findings are
downgraded to INFO. Unvetted versions retain their severity.
Lock-vs-ledger disagreement: lock wins. See the deps-vet skill's
hex-vet schema doc for the "Lock-vs-ledger disagreement" section.
Use /phx:deps-vet <pkg> <version> (separate skill) to add entries.
When run with DIFFERENTIAL=1 (default), findings that existed in the
OLD tarball are downgraded to INFO. Net-new signals reach the renderer
at full severity. See ${CLAUDE_SKILL_DIR}/references/differential.md.
For packages where the aggregate score exceeds 10, the
hex-deps-triager sonnet agent reads finding + diff windows and
produces structured verdicts (confidence, verdict, rationale,
fp_reasons[]). A context-supervisor (haiku) consolidates verdicts
across packages into triage/consolidated.md. Main skill reads only
the consolidated file.
See ${CLAUDE_SKILL_DIR}/references/llm-triage.md.
Per-package weighted sum: BLOCK = 10, WARN = 3, INFO = 1. Risk band: 0 clean · 1–5 low · 6–15 medium · 16+ high.
Output:
pkg | old → new | risk | findings | diff.hex.pm | maintainer-change plus a per-package detail section for any non-clean row..claude/deps-audit/last-run.json. The Phase 3 gate reads this; an audit that doesn't write it is a no-op for the gate. Always emit, even on clean runs.--json flag emits JSON to stdout instead of markdown. See
${CLAUDE_SKILL_DIR}/references/output-renderer.md for table format,
sidecar schema, exit-code rubric, and --quiet mode.
mix.lock, mix.exs, or any project file (non-mutating)mix deps.{get,update,compile} via deps-audit-gate.sh. See ${CLAUDE_SKILL_DIR}/references/hook.md./phx:compound after BLOCK findings — corpus self-feeds.--sarif <path> and gate CI via --ci.${CLAUDE_SKILL_DIR}/references/heuristics.md — full 35-rule catalogue${CLAUDE_SKILL_DIR}/references/rules-impl.md — bash + mix run -e for the 8 MVP rules${CLAUDE_SKILL_DIR}/references/operating-modes.md — Mode A/B/C resolver${CLAUDE_SKILL_DIR}/references/diff-resolver.md — shell snippets, lock parser${CLAUDE_SKILL_DIR}/references/tarball-fetcher.md — fetch wrapper, parallel cap, cache prune${CLAUDE_SKILL_DIR}/references/external-tools.md — mix_audit, osv-scanner wrappers${CLAUDE_SKILL_DIR}/references/hex-api.md — endpoint contracts, rate limit, Rule 6/8${CLAUDE_SKILL_DIR}/references/output-renderer.md — markdown, JSON v1, exit codes, SARIF${CLAUDE_SKILL_DIR}/references/testing.md — smoke runner, fixture matrix${CLAUDE_SKILL_DIR}/references/differential.md / llm-triage.md — Phase 2 NDJSON subtract + triager${CLAUDE_SKILL_DIR}/references/semgrep.md / yara.md — Phase 2 precision layers (soft deps)${CLAUDE_SKILL_DIR}/references/cassettes.md / sarif.md / hook.md / ci-integration.md — Phase 3 surface${CLAUDE_SKILL_DIR}/references/trusted-publishers.md / skill-checklist.md — upstream + eval${CLAUDE_SKILL_DIR}/references/audit-tmpdir.md — Phase 5 per-run ephemeral storage contract${CLAUDE_SKILL_DIR}/references/execution-flow.md / differential-cve.md — Phase 5 default scan + CVE difftools
Compatibility alias for the Elixir/Phoenix plugin's LiveView assigns audit. Invoke explicitly with /lv:assigns.
development
Trace Elixir call trees from entry points via mix xref. Use when debugging data flow, planning signature changes, or understanding how a bug reaches code.
tools
Compatibility alias for the Elixir/Phoenix plugin's N+1 query checker. Invoke explicitly with /ecto:n1-check.
tools
Compatibility alias for the Elixir/Phoenix plugin's Ecto constraint debugger. Invoke explicitly with /ecto:constraint-debug.