Packages
mob_dev
0.6.21
0.6.23
0.6.22
0.6.21
0.6.20
0.6.19
0.6.18
0.6.17
0.6.16
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.17
0.5.16
0.5.15
0.5.14
0.5.13
0.5.12
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.0
0.3.37
0.3.35
0.3.34
0.3.33
0.3.28
0.3.26
0.3.23
0.3.21
0.3.19
0.3.18
0.3.17
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.18
0.2.17
0.2.15
0.2.14
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.0
Development tooling for the Mob mobile framework
Current section
Files
Jump to
Current section
Files
lib/mob_dev/security_scan/finding.ex
defmodule MobDev.SecurityScan.Finding do
@moduledoc """
A single normalized security finding.
Findings come from many sources — Hex `mix_audit`, `osv-scanner`,
the OpenSSL/SQLite/Erlef advisory feeds, semgrep, etc. — and are
normalized into this struct so the report and rubric treat them
uniformly. `source` records which scanner produced it; `layer`
records which surface area it covers (`:hex_deps`, `:bundled_runtime`,
`:c_source`, ...).
`severity` is one of `:critical`, `:high`, `:medium`, `:low`,
`:unknown`. Scanners report severity differently (CVSS scores,
GHSA ratings, vendor scales); upstream callers normalize before
building a Finding.
"""
@type severity :: :critical | :high | :medium | :low | :unknown
@type t :: %__MODULE__{
id: String.t() | nil,
severity: severity(),
package: String.t() | nil,
version: String.t() | nil,
fixed_in: String.t() | nil,
title: String.t() | nil,
description: String.t() | nil,
url: String.t() | nil,
source: atom(),
layer: atom()
}
@derive Jason.Encoder
defstruct id: nil,
severity: :unknown,
package: nil,
version: nil,
fixed_in: nil,
title: nil,
description: nil,
url: nil,
source: nil,
layer: nil
@severity_order %{critical: 0, high: 1, medium: 2, low: 3, unknown: 4}
@doc """
Sort order helper: severities ranked critical → unknown.
Use as `Enum.sort_by(findings, &Finding.sort_key/1)`.
"""
@spec sort_key(t()) :: {non_neg_integer(), String.t()}
def sort_key(%__MODULE__{severity: sev, id: id}) do
{Map.get(@severity_order, sev, 99), id || ""}
end
@doc """
Deduplication key. Two findings dedupe to the same key when they
describe the same advisory against the same package+version, even
if they came from different sources (e.g. mix_audit and osv-scanner
both reporting GHSA-XXXX against `:plug` 1.10).
"""
@spec dedupe_key(t()) :: {String.t() | nil, String.t() | nil, String.t() | nil}
def dedupe_key(%__MODULE__{id: id, package: package, version: version}) do
{id, package, version}
end
end