Secrets in the checkout
Secrets found in the checkout, read from the report a secret scanner already left behind, or collected by running gitleaks or trufflehog when the runner carries one and no report is there. Only the rule, the file, the line and a fingerprint are collected: the matched value never is, because these facts are attached to the run event.
| Facts key | secrets |
| Version | v1 |
| Script | secrets.py |
| Timeout | 300 seconds |
Tools
This collector reads what these tools report. It never installs one: a tool that is not on the runner is missing evidence, and the guardrails asking for these facts skip rather than fail.
| Tool | Needed | Description |
|---|---|---|
gitleaks | optional | Secret detector, run over the checkout when no report is there. Preferred over trufflehog because it reports SARIF. |
trufflehog | optional | Secret detector that verifies what it finds against the service it belongs to, run when gitleaks is not there. |
Installing gitleaks
Install gitleaks with brew install gitleaks, or docker run --rm -v $(pwd):/repo zricethezav/gitleaks:latest detect --source /repo, or run the gitleaks/gitleaks-action@v2 step before this one and leave its report in the checkout.
Installing trufflehog
Install trufflehog with brew install trufflehog, or curl -sSfL https://raw.githubusercontent.com/trufflesecurity/trufflehog/main/scripts/install.sh | sh -s -- -b /usr/local/bin, or run the trufflesecurity/trufflehog@main step before this one.
Inputs
| Input | Description | Default | Environment |
|---|---|---|---|
reports | Comma separated globs matched against every file in the working directory and against bare file names, naming the secret scanner reports to read. Narrow it when several reports are present and only one of them should decide. | gitleaks*.sarif,gitleaks*.json,trufflehog*.json,.secrets.baseline | GUARDRAIL_INPUT_REPORTS |
scanHistory | Whether a tool the collector runs scans the repository's git history rather than the working tree. Reading the history finds a secret that was committed and then removed, and costs the whole log. Ignored when a report was found, because the report is what the pipeline already acted on. | false | GUARDRAIL_INPUT_SCANHISTORY |
maxFindings | Most findings to carry in the facts. The counts stay whole when findings are dropped. | 200 | GUARDRAIL_INPUT_MAXFINDINGS |
A guardrail that declares an input of the same name passes it through, so the guardrail's configuration in buildnote.json is what decides these values.
A guardrail asks for these facts by name, and reads them back the same way:
{
"collect": ["secrets"]
}secrets = guardrail.facts("secrets")Facts
The document secrets collects. A path carrying [] is an entry of the list before it, and one carrying [path] is a key of the object before it.
| Fact | Meaning |
|---|---|
source | report when a secret scanner's report was read, tool when the collector ran one itself, none when there was neither. |
reason | Why nothing was collected, present only when source is none. It names the globs that matched no readable report and the tool to install. |
reports | Every report that was read, in path order. |
reports[].path | Path of the report, relative to the directory the CLI runs in. |
reports[].modified | When the report was last written, ISO 8601, so a stale report is recognisable. |
reports[].format | Format it was read as: sarif, gitleaks, trufflehog or detect-secrets. |
tool.name | Tool that was run, present only when no report was found and one was on the PATH. |
tool.version | Version that tool reported when the CLI probed it, which is the answer to which scanner decided this build was clean. |
tool.path | Where on the PATH it was resolved to. |
tool.args | Arguments it was run with, so the same scan runs by hand. |
tool.exitCode | Code it exited with. A secret detector exits non zero when it finds something, so this is a result rather than a failure. |
tool.durationMs | How long the invocation took. |
counts.total | Secrets found, across every report that was read or everything the tool printed. |
counts.bySeverity.critical | Findings the scanner verified against the service the credential belongs to, so it is known to be live. |
counts.bySeverity.high | Findings reported without being verified, which is what a secret detection usually is. |
counts.bySeverity.medium | Findings the report rated medium. |
counts.bySeverity.low | Findings the report rated low. |
counts.bySeverity.info | Findings the report rated informational. |
counts.bySeverity.unknown | Findings the report gave no severity, or one nobody recognises. |
findings | The findings themselves, up to maxFindings. |
findings[].id | Rule the scanner matched, such as aws-access-token, or the detector that matched it. |
findings[].severity | critical, high, medium, low, info or unknown. |
findings[].message | What the scanner said about the rule, truncated to 500 characters. Never the value it matched. |
findings[].path | File the secret was found in, relative to the working directory, or null. |
findings[].line | Line it was found on, or null. |
findings[].fingerprint | Stable identity of the finding across runs, derived from the rule, the file and the line, so the same secret is recognisable in a later build without carrying it. |
dropped | Findings left out because maxFindings was reached. |
A collector that cannot finish prints what it had along with an incomplete key saying why, so the facts below the point it stopped are absent. A check that cares reads incomplete before the rest.
Example facts
The example facts collected by secrets collector:
{
"source": "report",
"reports": [
{
"path": "gitleaks.sarif",
"modified": "2026-03-04T10:15:00Z",
"format": "sarif"
}
],
"counts": {
"total": 1,
"bySeverity": {
"critical": 0,
"high": 1,
"medium": 0,
"low": 0,
"info": 0,
"unknown": 0
}
},
"findings": [
{
"id": "aws-access-token",
"severity": "high",
"message": "aws-access-token has detected a secret in service/src/main/resources/application.yaml.",
"path": "service/src/main/resources/application.yaml",
"line": 14,
"fingerprint": "19d0004f552705be"
}
],
"dropped": 0
}Collected for
| Guardrail | Category | Inputs |
|---|---|---|
secrets/detector-ran | secrets | none |
secrets/no-hardcoded-credentials | secrets | none |