Secrets, SBOM & image overlap
Where scanners meet.
A hardware store keeps plumbing in one aisle and electrics in another. Security scanning used to work the same way, one tool per aisle: IaC (infrastructure as code, the Terraform and Kubernetes files that describe your cloud) misconfigurations, leaked secrets, SBOM (software bill of materials, the ingredient list for what you shipped), and container CVEs (Common Vulnerabilities and Exposures, the public catalogue of known software flaws). Now every shopkeeper stocks the neighbouring aisle. Checkov scans secrets and emits an SBOM. Trivy scans IaC and images. The overlap is real, and without a map you either run the same check three times or leave one aisle completely empty.
Say the secrets scanner, the IaC scanner and the image CVE scanner all flag the same line of the same Dockerfile. That is three tickets, three suppressions written in three different syntaxes, and one very tired engineer. Deciding on purpose who covers what is how you keep the signal from turning into busywork.
Checkov's home turf is IaC misconfiguration. Its secrets and SBOM output are useful extras, never replacements for Gitleaks or Syft. Give each domain exactly one owner so the same risk never lands in your inbox three times.
Checkov's side jobs: secrets and SBOM
The secrets framework runs detect-secrets (regex patterns plus an entropy score for random-looking strings) across every file in the tree, not only the infrastructure code. Before it scans, Checkov builds a graph of your infrastructure code, wiring each resource to the variables and modules that feed it so a value can be followed across files. The CycloneDX and SPDX output, two standard SBOM file formats, lists what that graph parsed: the files and resources of your infrastructure, not the libraries inside your application. Name your frameworks on the command line so the scan stays fast and the output stays scoped to what you asked for.
$ checkov -d . --framework terraform,secrets --compact$ checkov -d . -o cyclonedx_json > sbom.cdx.json
terraform scan results:Passed checks: 812, Failed checks: 37secrets scan results:Passed checks: 1042, Failed checks: 1Check: CKV_SECRET_6: "Base64 High Entropy String"FAILED for resource: /app/config.yml# sbom.cdx.json written: the files and resources Checkov parsed, not app deps
Where Checkov runs out of road: image CVEs
Checkov's SBOM mirrors the graph it parsed, not your application's dependency tree. The sca_image and sca_package frameworks (SCA is software composition analysis, matching the components you ship against a vulnerability database) phone home to Prisma Cloud and need BC_API_KEY set. Without that key they do nothing at all. The purpose-built lane is Syft plus Grype, or Trivy covering image, config and filesystem from a single binary.
$ syft nginx:1.27 -o cyclonedx-json=sbom.cdx.json$ grype sbom:./sbom.cdx.json --fail-on high$ checkov --framework sca_image --docker-image nginx:1.27 --dockerfile-path ./Dockerfile --bc-api-key "$BC_API_KEY" --repo-id org/repo
✔ Vulnerability DB downloadedNAME INSTALLED FIXED-IN VULNERABILITY SEVERITYlibssl 1.1.1 1.1.1w CVE-2024-... Highcheckov sca_image: Passed 0, Failed 0 # without BC_API_KEY: framework skipped
Wiring the lanes together
Let Checkov own IaC misconfiguration. Let Gitleaks in a pre-commit hook own secrets at commit time, with Checkov's secrets framework as the CI (continuous integration) backstop behind it. Let Syft and Grype, or Trivy, own SBOM and image CVEs. Then drop the parsers that are now dead weight: --skip-framework sca_image,sca_package. Leave secrets on, unless a second tool is already sweeping for secrets in CI, in which case add secrets to that same list and let the other tool have the lane.
$ checkov -d . --skip-framework sca_image,sca_package$ gitleaks git --pre-commit --staged --redact -v
Passed checks: 812, Failed checks: 37# gitleaks, staged changes onlyINF scan completed in 12.4msINF no leaks found
Secrets: block at the door, sweep afterwards
checkov --framework secrets runs detect-secrets over the whole tree and makes a decent CI backstop. Gitleaks in a pre-commit hook, or server-side push protection, stops a credential before it reaches git history, where the only honest fix left is rotating it. The hook has to read the staged diff, so the command is gitleaks git --pre-commit --staged. Builds before 8.19 spell that same scan gitleaks protect --staged. Plain gitleaks git, which used to be called gitleaks detect, walks commits that already exist, so it sweeps rather than blocks. Run both layers and say out loud which one does which job: prevent at commit, detect in CI. Neither one covers for the other.
$ gitleaks git --pre-commit --staged --redact$ checkov -d . --framework secrets --compact --quiet
INF scan completed in 12.4msINF no leaks foundsecrets scan results:Passed checks: 1042, Failed checks: 0
The SBOM lane: Syft and Grype
The SBOM an auditor actually wants comes from Syft run against your build artefact or image, with Grype or Trivy matching CVEs against it, archived once per release. Checkov's CycloneDX is evidence of what your infrastructure code referenced, which is supplementary rather than a substitute.
Keeping --skip-framework honest
Once Trivy owns images, drop sca_image and sca_package from Checkov so one finding does not turn into three PR (pull request) comments. Add secrets to that list on the day another tool starts sweeping for secrets in CI, and not before. Write the ownership map into the repo README or a comment in .checkov.yaml, so the next engineer does not switch sca_* back on chasing "more coverage."
trivy fs --scanners secret,vuln,misconfig sweeps three lanes in one pass, which is genuinely appealing when your platform team is four people. Bigger estates still want a named owner per lane, so comments are not triplicated and every suppression is written in one syntax you can grep for.
Aim for at most one authoritative result per domain per pull request: one misconfiguration thread, one secrets hit, one image CVE summary. Duplicated gates teach developers to ignore all of them equally.
If you would rather trivy fs --scanners secret did the CI sweeping, that is fine, but then Checkov's secrets framework goes in the skip list and stays there. Put the image CVE gate on the job that builds the image rather than on the Terraform job, even though Checkov sca_image could technically run there with BC_API_KEY set.
These extras stay shallow because they ride along on a scan whose real job is misconfiguration. detect-secrets gives Checkov regex and entropy checks over every file, and the same run can drop a CycloneDX or SPDX artefact on its way out. Both come free with work already happening, and the depth matches the price. Selecting frameworks explicitly stops that free ride from slowing the scan you actually care about.
A pipeline layout that works
A sane default for a shop running Terraform, Kubernetes and containers is three jobs. Job 1 runs checkov -d . --framework terraform,kubernetes --skip-framework sca_image,sca_package,secrets on every infrastructure pull request. Job 2 owns the whole secrets lane here, with the Gitleaks hook on developer machines and gitleaks git on every push, which is why Job 1 hands secrets over instead of sweeping again. Job 3 runs syft and grype against the image tag you built. Each job uploads SARIF (Static Analysis Results Interchange Format, the standard file that code-scanning dashboards read) under its own category, or deduplicated by tool name. Developers then learn three comment threads instead of one thread showing the same bucket finding three times over from Checkov, Trivy config and Trivy fs.
$ checkov -d . --framework terraform,kubernetes --skip-framework sca_image,sca_package,secrets -o sarif --output-file-path .$ trivy image --format sarif -o trivy-image.sarif myapp:${{ github.sha }}
Wrote SARIF output to ./results_sarif.sarifWrote trivy-image.sarif# IaC findings from Checkov; CVE findings from Trivy image — separate artefacts
Notice what Job 1 is not doing. Because Job 2 owns secrets from the developer's machine through to every push, the infra job skips that framework alongside the sca_* pair, and the pull request gets one Checkov thread about misconfiguration and nothing else. Delete Job 2 tomorrow and secrets comes back out of that skip list the same day.
When leadership asks for "one scanner", put a number on the cost of overlap. Three tools reporting the same public S3 bucket produce three suppressions in three syntaxes, and somebody has to keep all three current. A documented ownership map beats a consolidation that triples the noise. And before you quote Checkov coverage in that meeting, look at the sca_* counts, because a framework that never ran also reports zero failures.
Try this
Run Checkov for infrastructure only, with the secrets and SCA frameworks skipped. Then run the commit-time secrets check, an image scanner, and Checkov's secrets sweep as its own step. Stage a file holding a fake key first, or the hook has nothing to read. What you want on screen afterwards is one artefact per lane, rather than three copies of the same alert.
$ checkov -d . --framework terraform,kubernetes --skip-framework secrets,sca_image,sca_package --compact --quiet$ gitleaks git --pre-commit --staged --redact || true$ trivy image myapp:local --severity HIGH,CRITICAL -f sarif -o trivy-image.sarif$ checkov -d . --framework secrets --quiet
terraform/kubernetes: Passed 1016, Failed 48INF scan completed in 9.8msINF no leaks foundWrote trivy-image.sarifsecrets: Failed 0# IaC job authoritative for misconfig; secrets/CVE have their own owners
Takeaway
Overlap is not free coverage. It is the same finding suppressed three times in three syntaxes, and those three suppressions go stale at three different rates. Pick one source of truth per domain: Checkov for IaC misconfiguration, a commit-time secrets scanner with a CI sweep behind it, and an image or SBOM tool for CVEs. Turning off Checkov's sca_* frameworks, and its secrets framework once another tool owns that sweep, is housekeeping, not a security regression.
Write the map down today, in the README or beside .checkov.yaml, while you still remember why each lane went where it did. Next, put the IaC lane behind a real merge gate with SARIF upload (cv-cicd), so the map becomes something the pipeline enforces instead of a diagram nobody reads.