Skip to content
Development
Skill

/phx-deps-audit

Audit Hex deps for supply-chain security risk — bidi chars, compile-time

From plugin
claude-elixir-phoenix
553101 skills30 agents2 commands
Install
$ npx -y skills add oliver-kriska/claude-elixir-phoenix --skill phx-deps-audit --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/phx-deps-audit

Context preview

The summary Claude sees to decide when to auto-load this skill.

Audit Hex deps for supply-chain security risk — bidi chars, compile-time

SKILL.md

phx-deps-audit.SKILL.md
name: phx-deps-audit
description: 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.

Hex Dependency Audit

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.

When to Use

  • After `mix deps.update` or `mix deps.get` brought in new versions
  • On PRs that touch `mix.lock` (pre-merge gate)
  • Before manually updating a single package (`--preview <pkg>`)
  • When investigating a dependency you don't recognize

Iron Laws

1. **NEVER claim a diff is clean without inspecting it.** Run all 8 rules on the unpacked NEW tarball. "Looks fine" without a tool run is a false pass. **Always write `.claude/deps-audit/last-run.json`** — its absence is evidence the audit didn't actually run. 2. **NEVER install `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. 3. **NEVER promote a finding to BLOCK without rule citation.** Every finding shows `rule_id`, `severity`, `file:line`, `snippet`, `message`. No handwaving. 4. **NEVER fetch from Hex API without rate-limiting.** Cap at 5 req/sec. Cache metadata 7 days, top-500 list 1 day. 5. **NEVER run the audit on already-committed lock changes silently** — tell the user which mode (A/B/C) is active and which `(old, new)` pairs resolved. 6. **LLM triage only above threshold.** Native rules + Semgrep + YARA are deterministic. The `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.

Operating Modes

| 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 `references/operating-modes.md` for full resolver logic.

Execution Flow

Default = full 8-rule scan with streaming progress. `--quick` opts out to CVE + retirement only. See `references/execution-flow.md`.

Step 1: Resolve the diff

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 `references/diff-resolver.md` for shell + `mix run -e` snippets per mode and the JSON output contract.

Step 2: Fetch tarballs (per-run tmpdir)

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 `references/audit-tmpdir.md` and `references/tarball-fetcher.md`.

Step 3: Run the 8 MVP rules on each NEW tarball

| # | 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 `references/heuristics.md`. Bash + `mix run -e` implementations for all 8 MVP rules in `references/rules-impl.md` (single-pass NEW + diff rules + Hex API rules, with `run_all_rules` master loop).

Step 4: External tool wrappers (parallel)

  • `mix hex.audit` — retired-package check, always available
  • `mix_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 `references/external-tools.md` for detection, output parsing, and severity mapping per tool.

Step 5: Hex API enrichment (per package)

  • `GET /api/packages/:name` — owners, downloads, inserted_at
  • `GET /api/packages/:name/releases/:version` — per-release publisher
  • Compute: `days_since_publish`, `owner_age_days`, `download_velocity`

Cap at 5 req/sec. Per-run cache under `${AUDIT_TMPDIR}/hex-api/`. See `references/hex-api.md` for endpoint contracts, caching strategy, Rule 6/8 detection, and Levenshtein implementation.

Step 5.5: Apply `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.

Step 5.7: Differential subtract

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 `references/di

Read more
Ships withclaude-elixir-phoenix

Docs: phxagents.dev -- install guides per runtime, the runtime compatibility matrix, all 26 Iron Laws, and a browsable skill and agent catalog. Claude Code is great.

Get the whole plugin

Other skills on claude-elixir-phoenix.