Mailglass.SupplyChain.AcceptedAdvisories (Mailglass v2.2.2)

Copy Markdown View Source

The single source of truth for advisories mailglass deliberately allows past mix hex.audit / mix deps.audit gates because no upstream fix exists yet. Both mix mailglass.publish.check (publish gate) and mix mailglass.audit (CI gate, dev/mix/tasks/mailglass.audit.ex) read this module exclusively — there is no second copy of the allowlist anywhere in the repo.

Each entry carries :id (the primary EEF-CVE id reported by hex.audit), :aliases (any GHSA ids the same advisory is known by in the mirego/elixir-security-advisories DB that mix deps.audit reads), :package, :severity, :reason, :accepted_on, and :recheck_by. Matching against a finding is exact :id-or-:aliases-member equality against ONE entry — never fuzzy, prefix, or cross-package.

Expiry and staleness

expired_entries/1 flags any entry whose :recheck_by date has passed (strictly after — an entry due today does not yet block). unused_entries/1 flags any entry that matched no current hex.audit finding across all three scanned Mix projects, so a suppression that is no longer needed cannot silently age forever. Both checks are local and deterministic — they never depend on OSV's unreliable fixed-event data.

Known limitation

unused_entries/1's "used" signal comes only from matched_hex_audit_ids/1, which is populated only by --kind hex runs of mix mailglass.audit. A future allowlist entry that is detectable ONLY by mix deps.audit (e.g. a GHSA-only id absent from the EEF-CVE database) would therefore be perpetually reported "unused" by every --kind hex run, pressuring a maintainer to delete a legitimately-needed suppression. This fails loud, not silent, so it does not reintroduce VULN-06's silent-aging defect for the two entries this phase ships (both EEF-CVE-keyed and hex.audit-native). It is nonetheless a real constraint on who may add future entries: if such an entry is ever needed, widen the "used" signal to also aggregate --kind deps matches — that widening is deliberately not built speculatively here.

Summary

Functions

Returns the full accepted-advisory allowlist.

Entries whose :recheck_by date has strictly passed as of today (Date.compare(today, recheck_by) == :gt) — an entry due today does not yet block; it blocks starting the day after.

Entries whose :id is absent from matched_ids — the aggregate of matched_hex_audit_ids/1 across every directory scanned by a single --kind hex run of mix mailglass.audit. An entry counts as "used" if it matched a finding in ANY scanned directory.

Types

entry()

@type entry() :: %{
  id: String.t(),
  aliases: [String.t()],
  package: String.t(),
  severity: String.t(),
  reason: String.t(),
  accepted_on: Date.t(),
  recheck_by: Date.t()
}

Functions

entries()

@spec entries() :: [entry()]

Returns the full accepted-advisory allowlist.

expired_entries(today \\ Date.utc_today())

@spec expired_entries(Date.t()) :: [entry()]

Entries whose :recheck_by date has strictly passed as of today (Date.compare(today, recheck_by) == :gt) — an entry due today does not yet block; it blocks starting the day after.

unused_entries(matched_ids)

@spec unused_entries(MapSet.t(String.t())) :: [entry()]

Entries whose :id is absent from matched_ids — the aggregate of matched_hex_audit_ids/1 across every directory scanned by a single --kind hex run of mix mailglass.audit. An entry counts as "used" if it matched a finding in ANY scanned directory.