I pointed a full scanning stack at a deliberately vulnerable test project last week — the kind of repo
that has a hardcoded AWS key in it, a public S3 bucket in the Terraform, an ancient Django version, and
an eval() on user input, all on purpose. Eight scanners, every category covered.
It came back with 223 findings. And that number is the entire problem with how most security scanning gets delivered.
223 lines of output is not a deliverable. It's a spreadsheet you email to someone who then has to become a security engineer for a week to figure out which twelve things actually matter. The scan is the easy part — it's about four minutes of compute. Everything that makes the result usable happens after.
Here are the three things that have to happen between the scan finishing and a report being worth paying for.
1. The same issue reported three times is not three issues
Serious scanning means running more than one engine per category. No single open-source scanner has complete policy coverage or a complete vulnerability database — so you run two for infrastructure-as-code, two for dependency CVEs, and reconcile the results. Coverage goes up. So does noise.
In that 223-finding run, 26 findings were the same underlying problem found by more than one engine. One line in one Terraform file — a bucket with a public ACL — got reported separately by three different scanners, each with its own wording, its own rule ID, and its own severity rating. Ship that raw and your client fixes one bucket and still has two open tickets about it.
2. Severity is not the same thing as urgency — and sometimes it isn't even severity
Most scanners export their results in SARIF, the industry-standard interchange format. It's a genuinely
useful spec, but it has a quirk that bites anyone who trusts it naively: SARIF's severity field is an
enum with four values — none, note, warning, and
error. There is no "critical".
So when a scanner finds a CVE with a CVSS score of 9.8, it has to write error in the
severity field, exactly like it would for a 7.1. The real score is still in the output — but it's tucked
into a per-rule properties bag, in a different place for every tool. Read the obvious field and every
critical in the report quietly becomes a high.
That's not a hypothetical. It's a bug I found in my own pipeline by cross-checking normalized output against what each scanner actually reported, and it had been silently capping severity on every critical finding. Anyone building on top of scanner output should go and check this right now.
3. A finding without a fix is just homework
This is the one that actually decides whether a security report is worth anything.
Most scanner output tells you what's wrong and stops. "S3 bucket has a public ACL." "Service account has excessive privileges." Technically correct, completely unactionable — the person reading it now has to go and research the fix, which is the expensive part, and which is exactly what they were paying someone else to already know.
A finding is only finished when it carries the fix with it. Three forms, depending on what's broken:
- Infrastructure as code — the actual snippet, ready to paste into the module.
- Live cloud configuration — the exact command, with the real resource names filled in.
- Everything else — the specific steps, in the right order. (Rotate the leaked credential before you rewrite git history. Do it the other way round and the key is still valid while you're busy cleaning up.)
For cloud-account findings, the same rule applies, plus one more thing worth carrying: which compliance controls the finding actually maps to. If a startup is working toward SOC 2 or ISO 27001, "this is CC6.1" is the difference between a security ticket and an audit-evidence ticket.
And the same remediation has to survive the export. A CSV that drops the fix column is just the wall of noise again, in a different file format — so every row carries its own remediation inline, ready to be split across a team without anyone having to go back to the PDF.
What this means if you're buying a security audit
Anyone can run the scanners. They're open source, they're free, and most of them are one
brew install away. If a security report you're paying for looks like it could have been
produced by piping a tool's default output into a PDF, it probably was.
The questions worth asking before you buy one:
- Does every finding come with a specific fix, or just a description of the problem?
- Has the output been deduplicated across engines, or am I getting the same issue three times?
- Are the severities the scanner's raw output, or has someone checked them against the real scores?
- Does it tell me the exact file and line — or the exact resource — so I can act without hunting?
- If I'm working toward a framework, does it tell me which controls each finding touches?
Findings are cheap. Fixes are the product.
This is the report format from a fixed-fee cloud security audit — read-only access, findings ranked by what's actually exposed, and an exact fix on every line.
See the cloud security audit →📢 Have questions or feedback? Drop a comment below or connect with me on Twitter/X@spysood!