Skip to main content

What auditors accept from a scanner, and what they do not

Most teams preparing a first audit discover the same gap in the same week. Someone runs a scanner, exports a PDF, drops it in the evidence folder, and the auditor asks: how often does this run, who looked at the results, what happened to the Critical on page four, and which control is this evidence for?

The frameworks are explicit about the cycle. A SOC 2 examination, per the AICPA, covers controls relevant to security, availability, processing integrity, confidentiality and privacy, and a control is something that operates over a period, not a document that exists on a date. ISO/IEC 27001:2022 has 93 Annex A controls, its transition deadline passed on 31 October 2025, and its new control A.8.28, Secure coding, requires security to be built into the development lifecycle rather than inspected at the end. In the United States, NIST SP 800-218, the Secure Software Development Framework, is the reference those auditors read. In Europe, the Cyber Resilience Act adds vulnerability-handling obligations across the product lifecycle, with reporting duties live since 11 September 2026.

This post is about what evidence from a code scanner an auditor will actually accept against those requirements, what Vulkro produces to meet it, and the controls no scanner can cover.

A finding list is half a control

Auditors are not checking whether you have a scanner. They are checking whether a control operates. For a secure development control that means four things with timestamps on each: a scan ran on a schedule or on every change; someone triaged the results; findings were fixed inside a defined SLA or formally accepted; and a later scan verified the fix. A single export shows step one and nothing else.

The second thing they want is mapping. "We scan for vulnerabilities" is not evidence for A.8.28, or for the SOC 2 change-management criteria, or for an SSDF practice. Evidence has to say: this control, these findings, this status, on this date. A PDF sorted by severity makes the auditor do that mapping, and auditors bill by the hour.

The third is repeatability. If the evidence cannot be regenerated by running the same command next quarter, it is a screenshot, and screenshots age badly.

A folder, not a screenshot

vulkro compliance-pack writes evidence as a directory rather than a report:

vulkro compliance-pack . --framework iso27001-2022 -o ./audit-q4

The output contains manifest.json (what ran, when, against which commit, with which scanner version), summary.md (a human-readable overview), findings.csv (every finding, one per row, for the auditor's own spreadsheet) and a controls/ directory with one JSON file per control, each listing the findings mapped to it with file:line evidence.

Each control gets one of three states, and the rule is mechanical so it can be explained in a sentence. A control passes when no findings map to it. It is partial when the only mapped findings are Medium or Low. It fails when any High or Critical finding maps to it. There is no judgement call in the file, which is the point: the auditor can re-derive the state from findings.csv.

The framework list covers the audits most teams face: soc2 and soc2-full, iso27001-2022, nist-800-53, nist-800-171, fedramp-moderate, pci-dss-4-0, hipaa, dora, nis2, hitrust and sox-itgc. Run the pack once per framework per quarter, commit the directory to the evidence repository, and the four-step cycle above falls out of the git history: the scan timestamp from manifest.json, the triage from the ticket links, the fix from the next quarter's pack showing the control moving from fails to passes.

For a view that is organised by profile rather than by framework file, vulkro compliance . --profile soc2 prints the mapping to the terminal. Profiles include owasp-asvs, owasp-web, pci, soc2, hipaa, nist-ssdf, nist-800-53, stateramp, iso27001, cis, cwe-top25 and gdpr. One property matters for its use in CI: compliance never exits 1 on findings. It is a reporting command, not a gate, so it can run on the nightly job without breaking it, while scan --fail-on does the gating elsewhere. vulkro report --profile soc2 renders the same mapping as a document when a reader wants prose.

Matching the evidence to the clause

The mapping is where a pack earns its keep, so it helps to know what each framework is asking for in code terms.

For ISO 27001 A.8.28, the question is whether secure coding is built into the lifecycle. Evidence: a pack per quarter showing the scan runs, and a CI configuration showing the scan runs on every change. The control files for injection, authentication and cryptography map to it directly.

For SOC 2, the common criteria around change management want to see that changes are tested before release. A scan result tied to a commit hash, with the gate decision recorded, is the evidence. The soc2-full framework adds the criteria that soc2 leaves out for teams that only need the core.

For SSDF, the practices are phrased as "review and/or analyze human-readable code" and "test executable code". The nist-ssdf profile maps findings to those practice identifiers so the auditor sees the practice name, not a CWE.

For the CRA, the lifecycle obligation means the evidence must show the handling of vulnerabilities over time, which is why a quarterly pack beats a single report: the sequence is the evidence.

What the scanner does not cover

The compliance-pack docs say it in four words: it covers code, not process controls. Take that seriously, because an auditor will.

A scanner cannot show that access to production was reviewed last quarter, that the new hire completed security training, that the vendor list was assessed, that the incident response plan was tested, or that the backup was restored. Those are process controls and the evidence for them is tickets, meeting records, HR exports and runbooks. A pack that marks every code control as passing says nothing about them, and a team that presents it as a full SOC 2 story will have a bad second meeting.

Within code, there are limits too. A control that passes means no finding mapped to it, not that the property holds. Vulkro's own benchmark reports a 79% found rate on planted vulnerabilities, so "passes" is a statement about what the scanner found. Say so in the evidence narrative. Auditors prefer a bounded claim they can verify to a broad one they cannot.

The Salesforce side

Orgs are in scope for the same audits, and the evidence has to come from metadata and configuration rather than application code. vulkro-sf compliance bundle soc2 produces the equivalent pack for an org. evidence-export writes a timestamped JSON that a CI job can archive on every run, and attest <FILE> records a SHA-256 attestation of an evidence file to ~/.vulkro-sf/attestations.log, so a reviewer can show that the file presented is the file produced.

What to do this week

  • Pick the one framework you are actually being audited against and run vulkro compliance-pack . --framework <name> -o ./audit-<quarter>.
  • Commit the output to an evidence repository so the timestamps and the diff between quarters become the cycle evidence.
  • Add vulkro compliance . --profile soc2 (or your profile) to the nightly job; it reports and never fails the build.
  • Write down, next to the pack, which controls are process controls and where their evidence lives.
  • If you run Salesforce, add vulkro-sf compliance bundle soc2 and attest to the same cadence.

The compliance-pack docs list every framework and the exact control identifiers each one writes. To produce your first pack, start the 14-day trial.