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

Computes the delta between the previous scan state and the
current report:

  * `new` — findings present now that were absent last run
  * `resolved` — findings present last run that are absent now
  * `still_present` — findings in both, with their `first_seen_at`
    preserved from the prior state for patch-lag display

The dedup key is `Finding.dedupe_key/1` (id, package, version).
Two findings reported by different sources for the same advisory
on the same package@version are considered the same finding —
resolution is based on the underlying vulnerability, not the
scanner that surfaced it.

# `t`

```elixir
@type t() :: %MobDev.SecurityScan.Diff{
  first_seen: %{required(MobDev.SecurityScan.StateFile.key()) =&gt; DateTime.t()},
  new: [MobDev.SecurityScan.Finding.t()],
  resolved: [MobDev.SecurityScan.StateFile.entry()],
  still_present: [MobDev.SecurityScan.Finding.t()]
}
```

# `compute`

```elixir
@spec compute(
  MobDev.SecurityScan.StateFile.state(),
  MobDev.SecurityScan.Report.t(),
  DateTime.t()
) ::
  t()
```

Compute the diff between a previous state map (typically loaded
from the state file) and the current report.

Both sides are keyed by the string form of `Finding.dedupe_key/1`
(`"id|package|version"`) so we can compare across the JSON state
file boundary.

`now` is injectable so tests can pin timestamps.

# `string_key`

```elixir
@spec string_key(MobDev.SecurityScan.Finding.t()) :: String.t()
```

String form of `Finding.dedupe_key/1` — matches StateFile entry keys.

---

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