Guide 065 explanation

Why a clean scanner report does not mean secure code

Interpret a clean scan by checking analyzed scope, raw alerts and filters, then write a release statement that preserves what remains unknown.

A scanner report described as clean may mean either that a run emitted no alerts or that no alerts match the dashboard's filter. Check which meaning applies before using it in a release decision. It does not mean every file was analyzed, every route is deployed safely, every business rule is enforced, or the application is free of vulnerabilities. The conclusion becomes more useful when the report names the revision, languages, analyzed paths, excluded paths, rule set, run status and independent checks. If those are missing, “clean” is mostly a dashboard label. The next decision is whether the run covered the risks you care about and which uncertainty requires another form of review.

OWASP's source analysis overview notes that static tools help find some flaw classes but can miss authentication, access-control, configuration and other context-heavy issues. A zero-alert result is therefore an observation about a method, not a security property of the whole application. The same caution applies to dynamic tools: OWASP's DAST guidance says a running-system scan cannot cover every source path and depends on which interfaces it can reach.

Illustrative case: the skipped export worker

Invented company and run; no real repository was scanned: Ridgeway Service has a TypeScript API and a Ruby worker. The CTO receives a green SAST dashboard for revision RS-107: “0 high findings.” The run completed without an engine error. The detailed configuration, however, enables TypeScript analysis only. The Ruby worker was discovered in the repository tree but not analyzed. That worker prepares customer-support archives after a request from the API.

The green dashboard does not prove the export worker safe or unsafe. It proves neither that an account-bound ownership check exists nor that it is absent. A separate deployment question also remains: production uses a feature flag that may route exports through a new storage path, but the scanner did not receive the flag state. The CTO's action should be to ask the service owner for the worker's code-review scope and the production route/configuration record. Until then, the correct statement is “no high alerts from the configured TypeScript analysis; Ruby worker and deployment-dependent export behavior were not assessed.”

Use this short coverage record whenever someone forwards a clean screenshot:

Field Ridgeway example Why it changes the conclusion
Revision and run RS-107, completed SAST run, dated 18 October 2026. Ties the result to one code state and tool configuration.
Analyzed scope TypeScript API paths shown in the analyzed-file ledger. Says what the engine actually processed.
Discovered but not analyzed Ruby export worker; language engine not enabled. A directory listing is not a security review.
Rules and thresholds Configured SAST rules; dashboard shows only high alerts. Medium/low alerts and unmodeled flaw classes require separate interpretation.
Deployment evidence Production export feature-flag value not supplied. Source alone cannot establish which path serves customers.
Independent review None yet for the account-bound export rule. “No alerts” does not establish the business control.
Next owner and check Platform owner supplies route/flag record; AppSec reviews worker and account ownership. Turns an unknown into an assigned task.

The record is deliberately modest. It does not infer a vulnerability from a skipped worker. It also prevents an unreviewed worker from disappearing into an apparently reassuring total.

Three green dashboards, three different decisions

A useful audit opens the run receipt behind the color. These miniature receipts are invented examples, not actual tool output:

Run A: revision RS-107; selected TypeScript files 40
       analyzed 40; completed; raw alerts 0; displayed alerts 0
Run B: revision RS-107; selected TypeScript 40 + Ruby worker 12
       analyzed TypeScript 40; Ruby unsupported; displayed high alerts 0
Run C: revision RS-107; selected TypeScript 40
       analyzed 40; completed; raw alerts 6; displayed high/open alerts 0

Run A performed the stated static work. It supports “no matches to the enabled checks in these 40 files.” If the release decision depends on account authorization through an export worker, that obligation still needs appropriate evidence; Run A never claimed to cover it. The next task is driven by the unreviewed business boundary, not by suspicion that every green result must hide a tool failure.

Run B did not assess the intended language scope. The worker was selected but unsupported. Counting all 52 discovered files as analyzed would be wrong. The next action is to use a supported method for the 12 worker files or narrow the release/assurance statement. The worker's absence from analysis is not evidence that it contains a vulnerability.

Run C is a display result, not a zero-alert run. Open the six raw candidates. Suppose two are rejected with controls, one is accepted risk, and three are unresolved medium candidates. The visible high/open count can honestly be zero while substantial review work remains. An accepted risk remains a known risk; a severity filter does not resolve an unreviewed claim.

Receipt Meaning of “zero” Release question still needing an answer
A No emitted alerts under the completed configured checks Were the material business/deployment obligations assessed separately?
B No displayed high alerts from the supported subset How will the skipped worker be assessed?
C No alerts match the chosen display filter What are the unresolved claims and accepted-risk conditions?

Now change a rule pack between releases. The old run emitted a query alert; the new run is quiet because that rule was disabled. That is not repair evidence. Compare the relevant source and the configuration before writing “fixed.” Conversely, if the control was actually corrected and the same rule ran, the disappearance is useful supporting evidence, though the changed path still deserves direct inspection.

The practical artifact is a two-line explanation of the numerator: raw candidate count, then displayed count with filter definition. Pair it with the analysis receipt. A dashboard screenshot without those facts cannot distinguish A, B and C.

A truthful clean-result statement

Replace “our code is secure” with a bounded statement such as:

At revision RS-107, the configured SAST run completed on the listed TypeScript API paths and produced no high-severity alerts under the enabled rules. It did not analyze the Ruby export worker. The production export flag and account-ownership behavior have not been independently verified. The platform owner will supply the deployment record, and AppSec will review the worker path before this result is used to characterize export risk.

That wording is not evasive. It tells leadership what was checked, what was not, and who will close the gap. OWASP's Web Security Testing Guide reporting chapter calls for scope and limitations as part of a useful assessment report. The same discipline belongs in a sprint update or buyer evidence packet.

If the team uses OWASP ASVS, do not mark every applicable requirement “passed” because a scanner emitted no alerts. ASVS is a set of verification requirements, and some controls need runtime, design, process or organizational evidence. A rule-to-requirement mapping can identify where to look; it does not show that a control was evaluated across the intended application.

Use the result at the actual release boundary

Ridgeway's CTO does not need to choose between calling the application secure and blocking every change indefinitely. Start with what this release changes. If it only adjusts the API's presentation and the worker is outside the stated review, the clean TypeScript result can contribute to that narrow decision. It cannot be used to answer a buyer's separate question about archive isolation. If the release enables a new export workflow, the unreviewed worker becomes directly relevant and needs an appropriate decision before the team describes that workflow as assessed.

Write the intended assertion before gathering more evidence. “The configured scan completed on the API revision being released” can be checked from run and build records. “Every export stays within its account” requires an account-boundary review spanning the worker and delivery path. “No customer data was exposed last month” requires historical evidence that a current source scan does not supply. These are different questions; one green report cannot answer all three by changing the wording of its cover page.

For the export enablement decision, Ridgeway could obtain a focused worker review, keep the feature disabled under a verified configuration, or have the accountable owner accept a clearly described uncertainty. The right choice depends on the actual business and exposure, not on an invented rule that every skipped file must block every release. Record what condition must remain true and what event requires reconsideration. A disabled feature stops being a useful boundary when another entry point can still invoke the worker.

Explain a later finding without rewriting the earlier record

Suppose the worker review later identifies a supported ownership flaw. Return to the earlier receipt before calling the TypeScript scanner a failure. The worker was explicitly outside Run A's scope; that finding does not demonstrate a miss in the 40 API files that were analyzed. It does demonstrate why the broader claim “the application is secure” would have been unsupported. The distinction helps the team improve coverage without misdiagnosing the tool or hiding a gap in its release process.

If the new flaw is instead in a selected API path, investigate whether the relevant rule ran, whether the engine modeled the framework, and whether the claim is within the tool's intended capability. Preserve the original result and the new finding. Changing today's dashboard to show the later discovery should not erase what the team actually knew at the earlier decision point. A useful retrospective connects the missed question to a specific improvement: a supported engine, a reviewed model, a business-rule check or a clearer release statement.

Likewise, a later rejection of a suspected flaw should narrow the concern without inflating the earlier scan's assurance. Evidence that a particular worker guard is effective closes that particular question. It does not prove that the API scanner had analyzed the worker all along. Keeping these histories separate makes future clean-result statements more credible and makes the next review easier to scope.

What to do when coverage or behavior is unknown

Have the AppSec owner ask five questions of the run: What revision was analyzed? Which intended paths were actually analyzed? Which rules and severities were active? Did every selected component finish? Which important actions require deployment or human evidence? The answer should fit in a small ledger like Ridgeway's. Assign missing artifacts to a person and date. For a skipped component, enable the supported engine or commission a focused source review; do not silently count it as covered. For a deployment-dependent control, get the actual selected build and configuration. If that is the only missing fact, rerunning the same source scanner adds no answer: obtain the deployment record, then decide whether a focused behavior check is needed. For a business rule, write the invariant and review the path that enforces it.

If a suspected high-impact exposure remains plausible while evidence is missing, discuss proportionate temporary controls with the service owner. Keep the technical status “investigation” until the path is established or defeated. If a later independent review confirms a flaw, open a supported finding. If it confirms an effective control for the assessed path, record the counterevidence. Neither outcome should be inferred from the green dashboard.

For the next clean report, ask for a one-paragraph bounded statement and the coverage ledger before sharing a color or count. Use the SAST primer to interpret the tool's signal, then the real-repository evaluation guide if the tool cannot show what it actually analyzed.