feat(elixir): gate on Hex's live advisory feed, not just mix_audit's mirror - #67
Merged
Conversation
…mirror `run-deps-audit` was being trusted as *the* dependency security gate, and it cannot carry that weight on its own. mix_audit resolves advisories from a single source — mirego/elixir-security-advisories, a sync of the GitHub Advisory Database — so an advisory that has not landed in that mirror is not reported at all. Seen on a consumer repo today: `mix deps.audit` printed "No vulnerabilities found." while `mix deps.get`, against the same mix.lock, flagged three advisories including a HIGH (bandit EEF-CVE-2026-74836, HTTP/2 connection- window starvation). The local mirror clone was at upstream HEAD, so this is not the stale-cache story it first looks like — a cache-bust or a fresh mix_audit build would have changed nothing. The data was never there. That is the dangerous shape of failure: not a gate that breaks, but a gate that returns green off data it did not read, on a signal people rely on. Adds `run-hex-advisory-check`, which parses the `VULNERABLE!` blocks Hex emits during resolution — a live feed, current by construction — and fails on any advisory not named in the new `hex-advisory-ignore` input. Ignores match the primary id or any `aka:` alias, so the GHSA ids consumers already pass to mix_audit's --ignore-advisory-ids work unchanged; ignored advisories are still printed as notices so they stay visible rather than vanishing. Learning from the bug it replaces, the parser refuses to fail open: if Hex reports advisories and the parser extracts none, the step errors on suspected output-format drift instead of passing quietly. Off by default — switching it on can red-line a repo with an untriaged advisory, so adoption stays deliberate and per-repo. run-deps-audit stays; the two sources are complementary and neither subsumes the other. Its input description now says so, so the next reader doesn't assume it is sufficient. Verified against captured Hex output: multiple advisories on one package, ignore-by-GHSA-alias, all-ignored, clean, and format-drift all behave. actionlint + shellcheck clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YEMvdSY4vBpcbE2cP1MP7A
2 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the detector half of HordiaLabs/scraper-control#69.
The problem
run-deps-auditis treated as the dependency security gate by consumers. It can't carry that alone.mix_auditresolves advisories from exactly one source —mirego/elixir-security-advisories, itself a sync of the GitHub Advisory Database. An advisory that hasn't reached that mirror isn't reported at all.Observed on
scraper-controltoday, samemix.lock, same machine:This is not the stale-cache story it looks like. The issue guessed that
mix_auditvendors its DB at build time and goes stale in the CI cache. It doesn't —MixAudit.Repogit-clones/pulls the mirror into~/.local/share/elixir-security-advisories-miregoon every run. The local clone was already at upstreamHEAD, and neither bandit advisory exists anywhere in that repo. Busting the build cache or rebuildingmix_audit, the issue's option 1, would have changed nothing.A gate that reports green off data it never had is worse than no gate, because CI is trusted.
The fix
run-hex-advisory-checkparses theVULNERABLE!blocks Hex prints during resolution. Hex queries the live feed on everydeps.get, so it's current by construction.hex-advisory-ignoretakes comma-separated ids, matched against the primary id or anyaka:alias — so the GHSA ids consumers already pass tomix_audit's--ignore-advisory-idswork verbatim, and the two lists stay in one dialect. (scraper-controlneeds this forGHSA-rhv4-8758-jx7v: decimal 3.0 has the fix, but ecto pinsdecimal ~> 2.0.)::notice::— they stay visible rather than vanishing.Defaults
Off by default, and
run-deps-auditstays. Turning this on can red-line a repo whose deps carry an untriaged advisory, so adoption should be per-repo and deliberate rather than arriving unannounced with a@v2bump. Worth flagging: every other Elixir repo on@v2has the same blind spot until it opts in.The two sources are complementary — neither is a superset — so the recommendation is both.
run-deps-audit's input description now says so explicitly, so the next reader doesn't assume it's sufficient.Testing
Parser exercised against real captured Hex output, all paths verified:
VULNERABLE!present but unparseableactionlint+shellcheckclean.🤖 Generated with Claude Code