A security scanner usually gives you a rule, a location, a severity, and a fix. That is a useful first pass. It is not the same as an answer to the question a developer actually has:
Is this thing real in this application, and what evidence can I show to the next reviewer?
Ship Safe 9.8 adds an investigation layer for that question. The existing deterministic engine remains the sensor layer. It still scans the repository locally, emits repeatable findings, and stays authoritative in CI. The new layer runs after detection and investigates the findings with bounded, cited passes.
The distinction matters because severity describes the impact of a pattern, not the truth of the conclusion. A high-severity rule match in a comment, a safe control, or a file that is never on the path can still be the wrong thing to block on.
Findings now carry evidence
Every investigation pass records a claim rather than writing an ad hoc answer onto the finding. A claim includes:
- the pass that made it
- a verdict such as
confirmed,likely,unresolved, orrefuted - a rationale
- citations to the files and lines that were read
- an attack path when the pass can trace one
The final verdict is derived from those claims. It is not whichever pass happened to run last, and it is not the most alarming string in the report.
The precedence is fixed
The passes have an explicit order:
1. reproduction
2. dataflow, chain, or presence
3. analysis
4. heuristic
A safe reproduction outranks a pattern match. A dataflow trace outranks a model's interpretation of the same code. A model's reading outranks a raw heuristic only when the higher-rank deterministic passes have not settled the question.
Equal-rank disagreement resolves to unresolved. If one pass says the path reaches a sink and another pass at the same rank says it does not, Ship Safe does not choose the scarier answer just to make the report look decisive.
A citation that does not resolve decides nothing
Evidence is only evidence if the cited file and line can be found in the repository being investigated.
If a pass returns a convincing rationale but its citation is stale, missing, or points outside the scan root, the claim is retained for diagnosis but excluded from verdict selection. That prevents a beautifully written explanation from becoming false authority after a file moves.
The output can therefore say two things at once: “this pass made this claim” and “the claim did not decide because its evidence could not be checked.” That is more useful than silently dropping the claim or presenting it as established.
The new commands
audit remains the broad scanner and score-oriented report. investigate is the evidence-first counterpart:
npx ship-safe investigate .
npx ship-safe investigate . --all
npx ship-safe investigate . --jsonThe default view groups findings by location and focuses on confirmed and likely results. --all includes unresolved and refuted findings. --json preserves the evidence for CI, dashboards, and later review. --deep adds the optional model analysis pass, but it does not get to suppress deterministic findings.
The grouping is intentional. Three rules firing on one eval() or one capability path are often one piece of work for the developer. The report should help them fix the path, not make the same line look like three unrelated emergencies.
Investigation follows the shape of the risk
The first passes are deliberately different because the rules ask different questions.
DataflowInvestigator traces a value backwards from a sink through assignments, destructuring, coercions, iterator call sites, and one function boundary. It can follow a request value into a helper in another file when the caller and parameter are visible. It stops at an unknown function call rather than mistaking an argument for a return value.
AbsenceInvestigator handles rules that assert a control is missing. It checks the whole project for a relevant application and for the control itself. A rate limit in a gateway may be outside the repository, so the strongest conclusion this pass can normally make is likely, not confirmed.
The capability pass looks across configuration files rather than pretending a single file contains the whole attack. A repository instruction, an always-allow runtime policy, an MCP write tool, and a credential are separate facts. Their combination is the security problem.
RedosReproducer can run a flagged regular expression against generated input in a worker with a hard deadline. A pattern match says that a dangerous shape exists. The bounded reproduction says whether the shape actually consumes the budget.
These passes do not all run on every finding. They engage when their question matches the finding, and abstaining is preferable to claiming a path that was not observed.
Fixes are verified by path closure
A security fix should be evaluated by what it changed in the path, not only by whether the rule became quiet.
The interactive fix agent now distinguishes:
resolved: the detector no longer finds the issueneutralised: the pattern still matches, but the investigated path is refutedunchanged: the evidence still standsweakened: the edit made the evidence uncertain, which is not proof of a fix
If a fix introduces a new confirmed finding, that is reported separately. The tool does not treat a quieter report as proof that the edit improved the repository.
CI can gate on what was established
Severity gates remain available. The new evidence gate is opt-in:
npx ship-safe ci . --fail-on-verdict confirmed
npx ship-safe ci . --fail-on-verdict likely
npx ship-safe ci . --fail-on-verdict noneSARIF output carries the verdict and the deciding pass so a GitHub code-scanning view can distinguish a traced confirmation from an unexamined heuristic match. --ignore-refuted is also opt-in. Refutation is useful evidence, but a wrong refutation can hide a real issue, so teams should choose that policy deliberately.
What this does not claim
An unresolved finding is not safe. A likely finding is not proof. A refuted finding is not deleted from history. The sensor result remains available, and the evidence explains why the investigation did or did not settle it.
The verdict benchmark is deterministic and gated, but it uses twelve synthetic scenarios and first-party labels. The application corpus is deliberately ungated because a confirmation can only be checked by reading the cited code. The investigation layer makes that reading easier; it does not pretend to replace it.
That is the release in one sentence: Ship Safe can now show the path behind a finding, and it is explicit when the path is not established.
Start with the CLI README, read the benchmark methodology, or run npx ship-safe investigate . in a repository you can inspect yourself.
Ship Safe is MIT licensed and works locally without an API key. The optional model pass is an addition to the evidence record, not an authority over deterministic findings.