Fail closed¶
A target that could not be scanned never reads as clean.
Silence has two causes — there was nothing to find, or nothing was looked at — and only one of them
is good news. Wherever saw cannot tell the two apart, it says so and fails the run rather than
returning the comfortable answer.
In practice:
- A file that cannot be read, a clone that failed, a malformed config, a worker that died mid-scan — each fails the run, never as clean.
- A scan-on-clone hook that hits its time budget reports the tree as unverified, not clean.
saw auditwithholds the all-clear when the start-up surface could not be established, and says which locations it could not account for.saw guard setuprefuses to write a workflow it cannot pin to an exact commit, rather than installing an unpinned gate.saw scan --require-dbturns a missing advisory corpus into a failure, for jobs that must not lose coverage quietly.
Where a check is not certain, saw warns loudly rather than degrading in silence. Warning noise is
recoverable; a false all-clear is not.
Why a CI gate is one line¶
saw scan has no --fail flag. The last line of the report is the verdict, unconditionally, so a
CI gate is one line with nothing to configure and no way to accidentally configure it away. The same
property makes the failure modes safe: an unscannable target and an infected one both fail the run,
so a gate that only ever checks "did this pass" already handles both.
The one deliberate default in the other direction is the advisory corpus: without it a scan continues
with less advisory coverage instead of failing, because advisories never gate anyway. --require-db
makes it strict when you need that.