# `MobDev.SecurityScan.Report`
[🔗](https://github.com/genericjam/mob_dev/blob/master/lib/mob_dev/security_scan/report.ex#L1)

Aggregate result of a security scan: every layer's `LayerResult`
plus run metadata. The report is the single object handed to
formatters (terminal, JSON, markdown) and the value returned
from `MobDev.SecurityScan.run/1`.

Severity rollup helpers (`severity_counts/1`, `worst_severity/1`)
are colocated here so formatters and the `--strict` exit-code
logic agree on the math.

# `t`

```elixir
@type t() :: %MobDev.SecurityScan.Report{
  finished_at: DateTime.t() | nil,
  layers: [MobDev.SecurityScan.LayerResult.t()],
  project_root: String.t(),
  started_at: DateTime.t()
}
```

# `all_findings`

```elixir
@spec all_findings(t()) :: [MobDev.SecurityScan.Finding.t()]
```

Flatten findings across all layers.

# `duration_ms`

```elixir
@spec duration_ms(t()) :: non_neg_integer() | nil
```

Total wall-clock duration of the scan in milliseconds, or nil if not yet finished.

# `maybe_exit_strict`

```elixir
@spec maybe_exit_strict(t(), boolean() | nil) :: :ok
```

If `strict?` is true and the report contains medium-or-worse findings,
print a message to stderr and `exit({:shutdown, 1})`. Otherwise returns
`:ok`. Shared between `mix mob.security_scan` and its `.log` sibling.

# `severity_counts`

```elixir
@spec severity_counts(t()) :: %{
  required(MobDev.SecurityScan.Finding.severity()) =&gt; non_neg_integer()
}
```

Count findings by severity across the report.
Returns a map keyed by `:critical`, `:high`, `:medium`, `:low`,
`:unknown` — every key is present (zero if no findings at that level).

# `worst_severity`

```elixir
@spec worst_severity(t()) :: MobDev.SecurityScan.Finding.severity() | :none
```

Worst severity present in the report. Returns `:none` when the
report has zero findings.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
