Since Prowler 5.32.0 the secret-scanning checks scan with Kingfisher. Earlier versions used the
detect-secrets library.Overview
Secret detection runs through a single helper inprowler/lib/utils/utils.py:
detect_secrets_scan_batch(payloads, excluded_secrets=..., validate=...)scans many payloads in chunked subprocess invocations and returns a{key: [findings]}dictionary. To scan a single payload, pass a one-entry mapping (for example,{0: data}).
detect_secrets_scan_batch amortizes that cost: it writes each payload to a temporary file as it consumes them, runs one subprocess per chunk (500 payloads by default), and maps the findings back to each payload by key.
The Batched Structure
Every secret-scanning check follows three phases.Phase 1: Collect
Define a generator that yields(key, payload) for each scannable unit. The generator builds payload strings only — it does not call Kingfisher. Lazy yielding keeps memory and temporary-disk usage bounded to a single chunk, which matters when an account holds thousands of resources.
Phase 2: Batch
Calldetect_secrets_scan_batch once with the generator. The helper consumes it in chunks, runs Kingfisher per chunk, and returns the keys that produced findings mapped to their finding lists.
Phase 3: Report
Iterate the resources, look up the findings by key, and build one report per resource. Emit a finding for every iterated resource — never drop one silently. When a resource’s payload cannot be prepared for scanning (for example, user data that fails to base64-decode or decompress), report it asMANUAL with a status explaining the scan could not inspect it, rather than omitting it or claiming PASS.
Choosing the Key
The key maps each finding back to its source. Two shapes cover every check:- One payload per resource: use the resource index. This fits checks that serialize a single payload per resource, such as launch configurations, CloudFormation outputs, SSM documents, Step Functions definitions, and OpenStack metadata.
- Several payloads per resource: use a
(resource_index, fragment)tuple, where the fragment identifies the variable, log stream, container, file, or version. Phase 3 groups the per-fragment findings to build the resource report. This fits CloudWatch log streams, ECS containers, CodeBuild variables, Glue arguments, and Lambda code files.
list(...) of resources in both Phase 1 and Phase 3 so the order stays stable and the keys align.
Preserving Per-Payload Results
detect_secrets_scan_batch runs Kingfisher with --no-dedup, so a secret that appears in more than one payload is reported for each one. This reproduces the result of scanning each payload individually. Build payload strings exactly as a single scan would: serialize the same data and keep line ordering, because messages often map a finding’s line_number back to a variable name or metadata key.
Validation and Severity
detect_secrets_scan_batch accepts validate, read from secrets_validate in the provider configuration or the --scan-secrets-validate flag. When enabled, Kingfisher confirms whether each secret is live, and confirmed secrets carry is_verified: True.
After marking a report as FAIL, pass the findings to annotate_verified_secrets(report, findings). When any secret is verified, the helper escalates the finding to critical severity and appends a note that the secret was confirmed live. Validation stays off by default because it sends the discovered secret to the provider API.
Excluded Secrets
detect_secrets_scan_batch applies secrets_ignore_patterns — regular expressions from the provider configuration — against each finding’s source line and drops the matches, mirroring single-scan behavior.
Testing
To assert on the verified-secret path, mockdetect_secrets_scan_batch in the check module and return the keyed dictionary. For a single resource scanned at index 0:
FAIL status and message, which exercises the real batched path. Refer to the Testing documentation for the general structure.
