deps-vet
Record a vetted Hex package version in hex_vet.exs after a security review — manages the audit ledger, not the scanner. Use to approve a dep after /phx:deps-audit findings or to initialize hex_vet.exs.
Install
npx skills add https://github.com/oliver-kriska/claude-elixir-phoenix/tree/main/plugins/elixir-phoenix/skills/deps-vet
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install oliver-kriska-claude-elixir-phoenix@llmmart
git clone https://github.com/oliver-kriska/claude-elixir-phoenix.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole oliver-kriska/claude-elixir-phoenix collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Deps Vet — Hex package audit ledger
Review a Hex package version, run Phase 1 supply-chain rules against it,
prompt the user for a verdict, append the result to hex_vet.exs
(project-root audit ledger). Vetted versions get downgraded to INFO
on subsequent /phx:deps-audit runs.
Run this AFTER /phx:deps-audit to clear findings.
Run this BEFORE merging a mix.lock PR to certify new versions.
Usage
/phx:deps-vet phoenix 1.7.21 # vet a single package version
/phx:deps-vet --seed # import curated baseline seed (~30 pkgs)
/phx:deps-vet --list # show existing ledger entries
/phx:deps-vet --check # cross-check mix.lock vs ledger
Iron Laws
- NEVER auto-approve. Every entry MUST come from an
AskUserQuestionconfirmation. Drive-by trust ruins the ledger's value. - Lock wins on disagreement. If
mix.lockhas version X and the ledger vets X-1, emit INFO and treat X as unvetted. Don't silently trust the older entry. - Ledger lives at project root.
hex_vet.exsis a first-class security artifact, visible in PR review. Don't move it into.claude/. - Round-trip via
inspect/2. When appending, read the file withCode.eval_file/1, mutate the map, and write back viainspect(term, pretty: true, limit: :infinity). Hand-rolled string appends drift over time. - Always show findings before prompting. The user must see what's
being vetted. No silent
:safe_to_deploydefaults. - Confirmation counts are COMPUTED, never estimated. Any number in
an
AskUserQuestion(criteria split, new/overwrite/no-op) MUST be derived from the loaded data before prompting — e.g.Enum.frequencies_by(seed.audits, & &1.criteria). Eyeballing the file and approving on wrong numbers corrupts the consent.
Execution flow
Step 1: Locate or seed hex_vet.exs
If hex_vet.exs exists at project root:
Read it via Code.eval_file/1
Else:
Write the empty-ledger stub (see ${CLAUDE_SKILL_DIR}/references/hex-vet.md §"Empty ledger")
Inform user: "Created hex_vet.exs at project root."
Step 2: Branch by mode
<pkg> <version>→ single-vet path (Step 3-7).--seed→ importpriv/hex_vet_seed.exs. Before prompting,Code.eval_file/1the seed and compute (Iron Law #6): thecriteriasplit (Enum.frequencies_by(seed.audits, & &1.criteria)) and, against any existing ledger, exact new / overwrite / no-op counts. Put those computed numbers in theAskUserQuestion. Also state up front that the seed is a provenance baseline, not certification of your currentmix.lock(per Iron Law #2, seed versions older than the locked ones stay unvetted). Ask before overwriting existing entries.--list→ render the audits table; exit.--check→ compare ledger entries withmix.lock; warn on drift. Read the lock viaCode.eval_file("mix.lock")with2>/dev/null— modern locks have quoted keys and emit afound quoted keywordwarning per package (tens of KB of noise that gets persisted as an oversized tool result otherwise).
Step 3: Fetch the tarball (single-vet)
Run the deps-audit corpus loader. Cache lives at
~/.cache/phx-deps-audit/corpus/<pkg>/<version>/contents/. Use:
bash ${CLAUDE_SKILL_DIR}/../deps-audit/scripts/fetch_tarball.sh \
<pkg> <version>
Step 4: Run Phase 1 rules
Source the rules from ../deps-audit/references/rules-impl.md.
Run run_all_rules over the cached dir. Write findings to a temp
vet-findings.jsonl. Set FINDINGS_FILE to override default path.
Step 5: Present findings
Print the findings table per ../deps-audit/references/output-renderer.md.
On zero findings: say "No findings — vet from a clean baseline."
On any finding: show severity, file, line, snippet inline.
Step 6: Prompt for verdict
Call AskUserQuestion with these 4 options:
:safe_to_deploy— full trust; findings investigated and cleared.:safe_to_run— trust in non-production envs only (test deps).:does_not_implement_crypto— Mozilla-style sub-criterion.Skip— defer decision; don't write an entry.
If any finding is BLOCK severity: default-highlight Skip. Require
explicit override before writing :safe_to_deploy over a BLOCK.
Step 7: Append to ledger
Read existing hex_vet.exs via Code.eval_file/1. Append the audit
map below to :audits. Write back via
Code.format_string!(inspect(...)).
%{
package: "<pkg>",
version: "<version>",
criteria: <verdict_atom>,
reviewer: "<git config user.email>",
notes: "<user-provided one-liner OR findings summary>",
reviewed_at: ~D[<today>]
}
Write back via Code.format_string!(inspect(term, pretty: true)).
Confirm to user: "Added <pkg> <version> to hex_vet.exs."
Integration
- Run after
/phx:deps-auditto clear vetted findings. - Run before merging a
mix.lockPR to certify new versions. - Run
/phx:deps-vet --checkto detect ledger drift vsmix.lock. /phx:deps-auditauto-downgrades vetted findings to INFO.policy.block_on_unvettedis enforced by the plugin'sdeps-audit-gate.shPreToolUse hook onmix deps.get/mix deps.update.
References
${CLAUDE_SKILL_DIR}/references/hex-vet.md— schema, parser, lookup${CLAUDE_SKILL_DIR}/references/seed.md—--seedflag, curated baseline${CLAUDE_SKILL_DIR}/../deps-audit/references/rules-impl.md— the same rules/phx:deps-auditruns
Out of scope (Phase 3+)
- Mix task surface — defer
mix phx.deps_vetto a separate Hex packagephx_deps_vetfor non-CC users. - Distributed imports — defer cargo-vet
imports:until trust-chain semantics are designed.
Files (claude-elixir-phoenix)
-
priv
-
hex_vet_seed.exs 7.4 KB · in bundle
-
-
references
-
hex-vet.md 11.2 KB
# `hex_vet.exs` — schema, parser, and lookup The audit ledger. Lives at **project root**, alongside `mix.exs` and `mix.lock`. Modeled on cargo-vet's `audits.toml` — same trust-chain intent, idiomatic Elixir surface. ## Why project root, not `.claude/` `hex_vet.exs` is a **deliverable security artifact**, not an ephemeral sidecar. Three properties of root placement that `.claude/` doesn't give us: 1. **Visible in PR review.** Adding a vetted dep shows up as a diff line on the same file as `mix.lock`, prompting the reviewer to look at both. 2. **CI-discoverable without configuration.** The triple `mix.lock` / `mix.exs` / `hex_vet.exs` is recognizable — security tooling can find the ledger without project-specific config. 3. **Survives `.claude/` deletion.** Some teams treat `.claude/` as per-developer state and gitignore it. The audit ledger has to be shared. Phase 1's `last-run.json` stays under `.claude/deps-audit/` intentionally — that file is ephemeral run state, not durable trust. ## Schema ```elixir # hex_vet.exs %{ imports: %{ # Phase 3+ feature — distributed audit imports. Ignored in Phase 2. # mozilla: "https://hg.mozilla.org/.../audits.toml" }, audits: [ %{ package: "phoenix", version: "1.7.21", criteria: :safe_to_deploy, reviewer: "oliver@ideax.sk", notes: "Reviewed against rules 1-8; diff.hex.pm checked clean.", reviewed_at: ~D[2026-05-12] }, %{ package: "jason", version: "1.4.4", criteria: :safe_to_deploy, reviewer: "team@example.com", notes: "No findings; widely-used (>50M downloads).", reviewed_at: ~D[2026-05-10] } ], policy: %{ criteria_required: :safe_to_deploy, block_on_unvetted: :new_only # Phase 3 default; see Tri-mode section } } ``` ### Tri-mode `block_on_unvetted` (Phase 3) `block_on_unvetted` takes one of four modes (a legacy boolean is normalized — see "Migration from Phase 2 boolean" below): | Mode | Hook behavior | When to pick | |------|---------------|--------------| | `false` | Warn-only; `mix deps.get` exits 0 | Phase 2 compat; opt-out | | `:new_only` | Block if PR ADDS an unvetted version; allow re-locks of already-locked unvetted pkgs | **Recommended default** for new projects | | `:strict` | Block ANY `mix deps.get` while any locked version is unvetted | Mature ledger; enforce on whole graph | | `:full` | Run Tier 2 audit pipeline (Semgrep + YARA + LLM) then apply `:strict` rules | High-stakes (financial, healthcare); accept 30-90s per `mix deps.get` | **Why `:new_only` is the default:** strict mode fails CI on existing repos before seed import; warn is too loose for teams serious about supply-chain. `:new_only` blocks the specific risk (introducing an unvetted dep) without holding the team hostage on historical un-audited locks. ### Migration from Phase 2 boolean The hook reads `policy.block_on_unvetted` and normalizes: ```elixir defp normalize_block_mode(false), do: false defp normalize_block_mode(true) do IO.warn(""" block_on_unvetted: true is deprecated and will be removed in v4.0. Replaced with :strict (same semantics). For new projects, consider :new_only — blocks only NEW unvetted versions, allows re-locks. """) :strict end defp normalize_block_mode(mode) when mode in [:new_only, :strict, :full], do: mode defp normalize_block_mode(other) do raise "Invalid block_on_unvetted: #{inspect(other)} — must be one of: false, :new_only, :strict, :full" end ``` The one-time `IO.warn` surfaces in `mix deps.get` output. Migrate to `:strict` (drop-in) or `:new_only` (recommended) to silence. ### `:new_only` semantics "New" means: the package+version pair in the **current** `mix.lock` was NOT in `mix.lock` at the hook's reference commit (default: `origin/main`, override via `PHX_DEPS_AUDIT_BASE`). The reference diff isolates additions: ```bash git show "${PHX_DEPS_AUDIT_BASE:-origin/main}":mix.lock 2>/dev/null \ > /tmp/mix.lock.base diff <(awk '/^ "[^"]+":/' /tmp/mix.lock.base) \ <(awk '/^ "[^"]+":/' mix.lock) \ | grep '^>' | sed 's/^> *//' ``` Each added line is one `<pkg>: <version>` pair; block if any added pair is missing from `audits`. Re-locks of already-locked unvetted pkgs are ignored — those are pre-existing tech debt, not new risk. ### Criteria atoms Following cargo-vet's vocabulary, three Phase 2 criteria are recognized: | Atom | Meaning | |------|---------| | `:safe_to_deploy` | Reviewed; safe in production. Highest trust. | | `:safe_to_run` | Safe in non-production envs (test/dev deps). | | `:does_not_implement_crypto` | Sub-criterion; package contains no cryptographic implementation, so transitive crypto-review obligations don't apply. | Other atoms are valid but unrecognized — Phase 2 treats them as a softer match (logged, never trusted). ### Empty ledger stub Used when `hex_vet.exs` doesn't exist. New ledgers default to `:new_only` (Phase 3) — opt-in to enforcement on the additions without blocking historical un-audited locks: ```elixir %{ imports: %{}, audits: [], policy: %{criteria_required: :safe_to_deploy, block_on_unvetted: :new_only} } ``` ## Parser Use `Code.eval_file/1` — Elixir's own parser, no Sourceror needed: ```bash # One-line read: mix run --no-mix-exs -e ' {ledger, _} = Code.eval_file("hex_vet.exs") IO.inspect(ledger.audits, limit: :infinity) ' ``` Inside a skill script the same call works via `mix run -e`. For lookup performance, the ledger is small (target: <2,000 entries; 50K LOC file). No streaming parser needed. ### Lookup function ```elixir def vetted?(ledger, pkg, version, required \\ :safe_to_deploy) do Enum.any?(ledger.audits, fn audit -> audit.package == pkg and audit.version == version and meets_criteria?(audit.criteria, required) end) end defp meets_criteria?(:safe_to_deploy, _required), do: true defp meets_criteria?(:safe_to_run, :safe_to_run), do: true defp meets_criteria?(other, other), do: true defp meets_criteria?(_, _), do: false ``` `:safe_to_deploy` satisfies every requirement (deploy implies run). `:safe_to_run` only satisfies `:safe_to_run`. ## Lock-vs-ledger disagreement (Day-1 decision: lock wins) When `mix.lock` says `phoenix 1.7.21` and the ledger has an entry for `phoenix 1.7.20`: - The unmatched lock version (1.7.21) is **unvetted**. - The orphaned ledger entry (1.7.20) is **informational** — emit an INFO finding "ledger entry exists for older version 1.7.20; treating 1.7.21 as unvetted." - The audit runs the full Phase 1 rule pass on 1.7.21 with normal severities. This is the conservative call. The alternative ("lock-version-or-higher trust") would let attackers exploit version-bump attacks where the ledger entry was approved on a safe version. ## Append flow Round-trip through `inspect/2` to preserve Elixir term semantics: ```bash mix run --no-mix-exs -e ' {ledger, _} = Code.eval_file("hex_vet.exs") new_audit = %{ package: "<pkg>", version: "<ver>", criteria: :safe_to_deploy, reviewer: System.cmd("git", ["config", "user.email"]) |> elem(0) |> String.trim(), notes: "<notes>", reviewed_at: Date.utc_today() } updated = Map.update!(ledger, :audits, &[new_audit | &1]) formatted = updated |> inspect(pretty: true, limit: :infinity, width: 80) |> Code.format_string!() |> IO.iodata_to_binary() File.write!("hex_vet.exs", formatted <> "\n") ' ``` `Code.format_string!/1` ensures the output matches the project's formatter config (incl. `.formatter.exs` overrides). Test the round-trip on a fixture before relying on it — version pinning matters. ## Migration from existing trust artifacts For projects using ad-hoc trust mechanisms (a comment in `mix.exs`, README sections, internal wiki pages), the seed-import flow (`/phx:deps-vet --seed`) lets a team bootstrap a real ledger from the top-100 list and then layer in project-specific audits. The seed is regenerated monthly; entries older than 90 days emit a stale-warning. See `seed.md` for the regeneration job. ## Distributed imports (Phase 3 — single-source v1) cargo-vet supports trusting other organizations' audit ledgers via the `imports:` table. Phase 3 ships **explicit allow-list v1**: any import URL listed in `imports` must be opted into per-project — no implicit trust, no transitive imports. ### Schema ```elixir imports: %{ # key = canonical handle, value = ledger URL "elixir-phoenix-plugin" => "https://raw.githubusercontent.com/oliver-kriska/claude-elixir-phoenix/main/plugins/elixir-phoenix/skills/deps-vet/priv/hex_vet_seed.exs" } ``` The handle (left side) is the attribution the renderer uses when a finding is downgraded via an imported audit: "vetted via `elixir-phoenix-plugin` (imported)". The URL (right side) must resolve to a file with the same `hex_vet.exs` map shape (`audits:` is the only key consumed; `imports:` of the imported ledger is ignored to prevent transitive trust chains). ### v1 allow-list Phase 3 v1 hardcodes the recognized import set: ```elixir @allowed_imports %{ "elixir-phoenix-plugin" => "https://raw.githubusercontent.com/oliver-kriska/claude-elixir-phoenix/main/plugins/elixir-phoenix/skills/deps-vet/priv/hex_vet_seed.exs" } ``` Imports listed in `hex_vet.exs` that aren't in `@allowed_imports` are **ignored with stderr warning**, never silently trusted. Multi-org imports (Phoenix team, EEF, etc.) stay drafted until the trust-chain semantics are battle-tested through one full cycle of single-import production use. ### Fetch + cache Imports fetch on first use and cache 24h under `.claude/deps-audit/cache/imports/<handle>.exs`. On cache miss or TTL expiry, re-fetch via `curl -fsSL`. Fetch failures fall back to the cached copy with a "stale import" stderr warning. ### Lookup precedence ```text project audits (hex_vet.exs `audits:`) ↓ not found imported audits (each allowed import, parallel) ↓ not found unvetted (Phase 1 rules apply at full severity) ``` A local audit always wins over an import — explicit project trust is more durable than implicit shared trust. Conflicts (local `:safe_to_run` vs import `:safe_to_deploy`) resolve to local. ### Attribution in output The renderer surfaces import provenance: ```text phoenix 1.7.21 — vetted via elixir-phoenix-plugin (imported), :safe_to_deploy plug 1.16.1 — vetted locally (oliver@ideax.sk, 2026-05-12), :safe_to_deploy unknown 0.1.0 — unvetted (no local or imported audit) ``` Reviewers see exactly where the trust came from. Out-of-policy imports (anything not in the allow-list) get a one-time stderr warning `ignored import: <handle> — not in v1 allow-list` but never silently downgrade severity. ### Publishing your org's audit ledger The plugin's seed file is the reference shape. Mirror the pattern: 1. Maintain an `hex_vet.exs` (or any `.exs` returning the same map) in a repository your team controls. 2. Open a PR to add your URL to the plugin's `@allowed_imports`. 3. Add a `README` covering: review process, criteria meaning for your org, signing/checksums. 4. Cross-reference: your `hex_vet.exs` lists `imports:` of `elixir-phoenix-plugin` for symmetry. Allow-list reviews enforce trust-chain hygiene: a malicious import URL would compromise every plugin user. The plugin maintainers are the final gate. -
seed.md 4.5 KB
# Seed ledger — curated baseline of vetted Hex packages `priv/hex_vet_seed.exs` ships with the plugin and provides a **curated baseline of audits** for a hand-picked set of high-trust, high-download Hex packages (currently ~30 — the monthly CI job in "Regeneration" grows this toward the top-100). It is a **provenance baseline, not certification of your project's current `mix.lock`.** Seed versions are pinned; per `hex_vet.exs` Iron Law #2 (lock wins), any locked version *newer* than the seed entry stays unvetted. On an up-to-date stack the seed may certify few or none of your actual locked deps — surface this to the user *before* import, not after. The seed's lasting value is the trusted provenance record + `:new_only` enforcement of *future* additions. ## Iron Laws (seed-specific) 1. **Seed entries are NOT a substitute for project-specific review.** They're a baseline; project-aware audits still take precedence. 2. **Stale seeds warn.** If `reviewed_at` is more than 90 days old at import time, emit a warning. Stale trust is more dangerous than no trust. 3. **Seed import is opt-in.** `/phx:deps-vet --seed` requires an explicit `AskUserQuestion` confirmation; never silent. ## Schema Same map shape as `hex_vet.exs`, but lives at `plugins/elixir-phoenix/skills/deps-vet/priv/hex_vet_seed.exs`: ```elixir # Auto-generated by .github/workflows/seed-regen.yml on YYYY-MM-DD. # Reviewer: <CI identity>. %{ generated_at: ~D[2026-05-12], source: "https://hex.pm/api/packages?sort=downloads", audits: [ %{package: "jason", version: "1.4.4", criteria: :safe_to_deploy, ...}, %{package: "phoenix", version: "1.7.21", criteria: :safe_to_deploy, ...}, # ... ~28 more ] } ``` ## Import flow ```text /phx:deps-vet --seed Step 1: Code.eval_file priv/hex_vet_seed.exs. Step 2: Check generated_at; warn if > 90 days. Step 3: COMPUTE before prompting (Iron Law #6): - total = length(seed.audits) - split = Enum.frequencies_by(seed.audits, & &1.criteria) - vs existing ledger: new / overwrite / no-op counts Step 4: AskUserQuestion (numbers are the computed values, never estimates): "Import <total> curated audits into hex_vet.exs? - <split[:safe_to_deploy]> :safe_to_deploy, <split[:safe_to_run]> :safe_to_run (+ any others) - <new> new · <overwrite> overwrite · <no-op> no-op Note: provenance baseline — seed versions are pinned; newer locked versions stay unvetted (lock wins)." Step 5: On Yes: dedupe + merge + write back via Code.format_string!. On No: exit without modification. Step 6: Confirm: "Imported <N> entries; ledger now has <total>." ``` Existing audits never get silently overwritten — overwrites surface in the confirmation prompt. ## Regeneration Monthly CI job (`.github/workflows/seed-regen.yml`): 1. Query `https://hex.pm/api/packages?sort=downloads` for top-100. 2. For each, run Phase 1 rules + check Hex retirement. 3. Compose audit entries (criteria: `:safe_to_deploy` if zero BLOCKs). 4. Open a PR to update `priv/hex_vet_seed.exs`. The job runs under the plugin's CI identity; the PR is human-reviewed before merge. No auto-merge for security artifacts. ### Org-policy 403 fallback Some org GitHub policies deny `GITHUB_TOKEN` PR creation. The seed- regen and cassette-regen workflows both apply the same fallback pattern when `peter-evans/create-pull-request@v6` exits with 403: 1. Upload the regenerated artifact (`priv/hex_vet_seed.exs` for seed, the cassettes tree for cassettes) as a workflow artifact. 2. Write a job summary explaining the policy and pointing to `Settings → Actions → Allow GitHub Actions to create and approve pull requests`. 3. Exit 0 — the regen succeeded even if the PR didn't. Maintainers download the artifact, commit manually. The fallback is the same in `cassette-regen.yml`; see `deps-audit/references/cassettes.md` "403 fallback". ## When to NOT use the seed - Air-gapped or fork-restricted environments. The seed assumes upstream Hex; if your registry is internal-only, audit your local catalog instead. - Hard-trust corporate-controlled packages — for those, project-level audits with internal reviewer emails are more legible in PR review. ## Future: distributed imports cargo-vet's `imports:` field lets organizations trust **each other's** audits via signed bundles. Phase 2 stubs the `imports:` key in the schema but ignores its contents. Phase 3+ wiring TBD.
-
-
SKILL.md 6 KB
--- name: deps-vet description: "Record a vetted Hex package version in hex_vet.exs after a security review — manages the audit ledger, not the scanner. Use to approve a dep after /phx:deps-audit findings or to initialize hex_vet.exs." argument-hint: "<pkg> <version> | --seed | --list | --check" effort: medium --- # Deps Vet — Hex package audit ledger Review a Hex package version, run Phase 1 supply-chain rules against it, prompt the user for a verdict, append the result to `hex_vet.exs` (project-root audit ledger). Vetted versions get downgraded to `INFO` on subsequent `/phx:deps-audit` runs. Run this AFTER `/phx:deps-audit` to clear findings. Run this BEFORE merging a `mix.lock` PR to certify new versions. ## Usage ```text /phx:deps-vet phoenix 1.7.21 # vet a single package version /phx:deps-vet --seed # import curated baseline seed (~30 pkgs) /phx:deps-vet --list # show existing ledger entries /phx:deps-vet --check # cross-check mix.lock vs ledger ``` ## Iron Laws 1. **NEVER auto-approve.** Every entry MUST come from an `AskUserQuestion` confirmation. Drive-by trust ruins the ledger's value. 2. **Lock wins on disagreement.** If `mix.lock` has version X and the ledger vets X-1, emit INFO and treat X as unvetted. Don't silently trust the older entry. 3. **Ledger lives at project root.** `hex_vet.exs` is a first-class security artifact, visible in PR review. Don't move it into `.claude/`. 4. **Round-trip via `inspect/2`.** When appending, read the file with `Code.eval_file/1`, mutate the map, and write back via `inspect(term, pretty: true, limit: :infinity)`. Hand-rolled string appends drift over time. 5. **Always show findings before prompting.** The user must see what's being vetted. No silent `:safe_to_deploy` defaults. 6. **Confirmation counts are COMPUTED, never estimated.** Any number in an `AskUserQuestion` (criteria split, new/overwrite/no-op) MUST be derived from the loaded data *before* prompting — e.g. `Enum.frequencies_by(seed.audits, & &1.criteria)`. Eyeballing the file and approving on wrong numbers corrupts the consent. ## Execution flow ### Step 1: Locate or seed `hex_vet.exs` ```text If hex_vet.exs exists at project root: Read it via Code.eval_file/1 Else: Write the empty-ledger stub (see ${CLAUDE_SKILL_DIR}/references/hex-vet.md §"Empty ledger") Inform user: "Created hex_vet.exs at project root." ``` ### Step 2: Branch by mode - **`<pkg> <version>`** → single-vet path (Step 3-7). - **`--seed`** → import `priv/hex_vet_seed.exs`. Before prompting, `Code.eval_file/1` the seed and **compute** (Iron Law #6): the `criteria` split (`Enum.frequencies_by(seed.audits, & &1.criteria)`) and, against any existing ledger, exact new / overwrite / no-op counts. Put those computed numbers in the `AskUserQuestion`. Also state up front that the seed is a **provenance baseline, not certification of your current `mix.lock`** (per Iron Law #2, seed versions older than the locked ones stay unvetted). Ask before overwriting existing entries. - **`--list`** → render the audits table; exit. - **`--check`** → compare ledger entries with `mix.lock`; warn on drift. Read the lock via `Code.eval_file("mix.lock")` with **`2>/dev/null`** — modern locks have quoted keys and emit a `found quoted keyword` warning per package (tens of KB of noise that gets persisted as an oversized tool result otherwise). ### Step 3: Fetch the tarball (single-vet) Run the deps-audit corpus loader. Cache lives at `~/.cache/phx-deps-audit/corpus/<pkg>/<version>/contents/`. Use: ```text bash ${CLAUDE_SKILL_DIR}/../deps-audit/scripts/fetch_tarball.sh \ <pkg> <version> ``` ### Step 4: Run Phase 1 rules Source the rules from `../deps-audit/references/rules-impl.md`. Run `run_all_rules` over the cached dir. Write findings to a temp `vet-findings.jsonl`. Set `FINDINGS_FILE` to override default path. ### Step 5: Present findings Print the findings table per `../deps-audit/references/output-renderer.md`. On zero findings: say "No findings — vet from a clean baseline." On any finding: show severity, file, line, snippet inline. ### Step 6: Prompt for verdict Call `AskUserQuestion` with these 4 options: - **`:safe_to_deploy`** — full trust; findings investigated and cleared. - **`:safe_to_run`** — trust in non-production envs only (test deps). - **`:does_not_implement_crypto`** — Mozilla-style sub-criterion. - **`Skip`** — defer decision; don't write an entry. If any finding is BLOCK severity: default-highlight `Skip`. Require explicit override before writing `:safe_to_deploy` over a BLOCK. ### Step 7: Append to ledger Read existing `hex_vet.exs` via `Code.eval_file/1`. Append the audit map below to `:audits`. Write back via `Code.format_string!(inspect(...))`. ```elixir %{ package: "<pkg>", version: "<version>", criteria: <verdict_atom>, reviewer: "<git config user.email>", notes: "<user-provided one-liner OR findings summary>", reviewed_at: ~D[<today>] } ``` Write back via `Code.format_string!(inspect(term, pretty: true))`. Confirm to user: "Added `<pkg>` `<version>` to hex_vet.exs." ## Integration - **Run after** `/phx:deps-audit` to clear vetted findings. - **Run before** merging a `mix.lock` PR to certify new versions. - **Run `/phx:deps-vet --check`** to detect ledger drift vs `mix.lock`. - **`/phx:deps-audit`** auto-downgrades vetted findings to INFO. - **`policy.block_on_unvetted`** is enforced by the plugin's `deps-audit-gate.sh` PreToolUse hook on `mix deps.get` / `mix deps.update`. ## References - `${CLAUDE_SKILL_DIR}/references/hex-vet.md` — schema, parser, lookup - `${CLAUDE_SKILL_DIR}/references/seed.md` — `--seed` flag, curated baseline - `${CLAUDE_SKILL_DIR}/../deps-audit/references/rules-impl.md` — the same rules `/phx:deps-audit` runs ## Out of scope (Phase 3+) - **Mix task surface** — defer `mix phx.deps_vet` to a separate Hex package `phx_deps_vet` for non-CC users. - **Distributed imports** — defer cargo-vet `imports:` until trust-chain semantics are designed.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.