Output Formats
Trestle can report findings in various formats. Pick one with --output-format=<name> on the command line, or with output-format = "<name>" in a configuration file. The default is text.
The output formats supported by Trestle are:
text- Findings formatted for reading in a terminal, with optional ANSI colors.csv- One finding per row, with a header row.json- A single JSON document containing every finding.junit- JUnit XML, suitable for CI systems that render JUnit reports.sarif- SARIF, used by code-scanning dashboards such as GitHub's.xml- A plain XML rendering of the findings.
Rule IDs
Every finding carries one of the following rule IDs. The ID identifies the kind of location the secret was found in, not the kind of credential.
secret-assignment- Secret assigned to a named variable, constant, parameter, or property.secret-value- Secret value found in source code.binary-secret- Secret found in a binary file.text-secret- Secret found in a text file.exposed-secret- Secret written to program output, where it may be exposed to users or other systems.
The rule ID value is the same in every format. How it is carried depends on the format:
csvwrites it in theRule IDcolumn.jsonwrites it as theruleIdfield on every diagnostic.xmlwrites it as theruleIdattribute on every<diagnostic>.junitwrites it as theclassnameattribute on every<testcase>.sarifwrites it asruleIdon every result and lists all the rules undertool.driver.rules.textdoes not include the rule ID. It writes the severity, location, and message only.
Severity
Every finding has one of two severities:
critical- the value can be identified as a credential from its own contents, without needing any other evidence.warning- the value is consistent with a credential, but identifying it as one relied on surrounding context (the name it was assigned to, neighbouring code, or a heuristic).
How severity is rendered depends on the format:
textuppercases the value (CRITICAL,WARNING).csv,json, andxmluse lowercase.junituses lowercase inside the<failure type="...">attribute.sarifuses its own vocabulary instead:criticalis written aserror, andwarningstayswarning.
Assignment types
For findings with rule ID secret-assignment, every record carries an assignment type that names where in source code the secret was found:
argument- a value passed to a function, method, or macro call.attribute- a value attached to a declaration through attribute syntax, such as a JSX element attribute.backend configuration- a value inside a Terraformbackendblock, which does not permit variable interpolation.build argument- a DockerfileARGdeclaration.constant- a binding declared with the language's constant form.directive- a configuration directive whose value follows the directive keyword as a bare token, as in a Redis configuration file.element- the contents of a structural element rather than a named field, such as XML element text or an entry in a collection literal.environment variable- a binding that defines a process environment variable, such as a DockerfileENVline or a dotenv entry.header- an HTTP header value.parameter- a parameter declared with a default value in a function, method, or block signature.property- a named field inside a structured data record, such as a JSON, YAML, TOML, or XML key.user- a credential bound to a named user account in an access control directive, such as a Redis ACL entry.variable- a variable binding.
The assignment type appears as a structured field in every format that carries one:
csvwrites it in theAssignment Typecolumn.jsonwrites it as theassignmentTypefield.xmlwrites it as theassignmentTypeattribute on the<diagnostic>.junitwrites it as theassignmentTypeattribute on the<testcase>.sarifwrites it underproperties.assignmentTypeon every result.
The assignment type is also embedded in the message string of every format.
Locations
Source locations are absolute file paths. For secret-assignment and secret-value findings, the record also carries a start line, start column, end line, and end column. Lines and columns are one-based. For binary-secret and text-secret findings, the record carries only the file path, because the match covers the whole file.
Summary
When --show-summary is enabled (the default), every format appends scan statistics:
scannedFileCount- Number of files read.elapsedMilliseconds- Wall-clock duration of the scan, in milliseconds.criticalCount- Number of critical findings.warningCount- Number of warning findings.totalCount- Total finding count.message- A human-readable summary line.
The summary is embedded in the document for json, sarif, xml, and junit. For text it is written after the findings on standard output. For csv it is written to standard error.
text
Findings are formatted for reading in a terminal. One line per finding, then an optional summary line.
WARNING src/api.ts:3:18 OpenAI key assigned to variable "apiKey".
CRITICAL keys/server.pem PEM private key found.
Scanned 12 files in 42ms. Found 1 secret and 1 warning.csv
The format is one header row and one finding per row. The summary line, if enabled, is written to standard error.
The columns are, in order:
Severity- the severity.Rule ID- the rule ID.Name- the identifier the secret was assigned to. Empty for rule IDs other thansecret-assignment.Assignment Type- the assignment type. Empty for rule IDs other thansecret-assignment.Description- a short label for the credential.Message- the human-readable finding message.File- absolute file path.Start Line,Start Column,End Line,End Column- the position of the match. Empty forbinary-secretandtext-secret.
When --explain is set, an Explanation column is appended.
Severity,Rule ID,Name,Assignment Type,Description,Message,File,Start Line,Start Column,End Line,End Column
warning,secret-assignment,apiKey,variable,OpenAI key,"OpenAI key assigned to variable ""apiKey"".",/repo/src/api.ts,3,18,3,68
critical,text-secret,,,PEM private key,PEM private key found.,/repo/keys/server.pem,,,,json
A single JSON document with two top-level keys: diagnostics (an array of findings) and, when the summary is enabled, summary.
The fields on each diagnostic are:
ruleId- the rule ID.severity- the severity.name- the identifier the secret was assigned to. Omitted for rule IDs other thansecret-assignment.assignmentType- the assignment type. Omitted for rule IDs other thansecret-assignment.description- a short label for the credential.message- the human-readable finding message.file- absolute file path.startLine,startColumn,endLine,endColumn- the position of the match. Omitted forbinary-secretandtext-secret.
When --explain is set, every diagnostic carries an additional explanation string.
{
"diagnostics": [
{
"ruleId": "secret-assignment",
"severity": "warning",
"name": "apiKey",
"assignmentType": "variable",
"description": "OpenAI key",
"message": "OpenAI key assigned to variable \"apiKey\".",
"file": "/repo/src/api.ts",
"startLine": 3,
"startColumn": 18,
"endLine": 3,
"endColumn": 68
}
],
"summary": {
"scannedFileCount": 12,
"elapsedMilliseconds": 42,
"criticalCount": 0,
"warningCount": 1,
"totalCount": 1,
"message": "Scanned 12 files in 42ms. Found 1 warning."
}
}junit
JUnit XML, suitable for CI systems that render JUnit reports. The document has one <testsuite> with one <testcase> per finding.
On the <testsuite>:
name="trestle".testsandfailuresboth equal the total finding count.errors="0".
On each <testcase>:
name- the source location (path:line:column) or the file path for whole-file findings.classname- the rule ID.assignmentType- the assignment type. Omitted for rule IDs other thansecret-assignment.
Each <testcase> contains one <failure> with the severity as the type attribute, the finding message in the message attribute, and the rendered finding line as CDATA. When --explain is set, the remediation guidance is appended to the CDATA body.
When summary is enabled, a <properties> block lists the same summary fields documented above.
<?xml version="1.0" encoding="UTF-8"?>
<testsuite name="trestle" tests="1" failures="1" errors="0">
<properties>
<property name="scannedFileCount" value="12"/>
<property name="elapsedMilliseconds" value="42"/>
<property name="criticalCount" value="0"/>
<property name="warningCount" value="1"/>
<property name="totalCount" value="1"/>
<property name="message" value="Scanned 12 files in 42ms. Found 1 warning."/>
</properties>
<testcase name="/repo/src/api.ts:3:18" classname="secret-assignment" assignmentType="variable">
<failure message="OpenAI key assigned to variable &quot;apiKey&quot;." type="warning">
<![CDATA[
/repo/src/api.ts:3:18 Warning: OpenAI key assigned to variable "apiKey".
]]>
</failure>
</testcase>
</testsuite>sarif
SARIF 2.1.0. Suitable for any tool that consumes the OASIS SARIF specification, including GitHub's code-scanning dashboard.
The document has one runs entry with:
tool.driver.name = "trestle"andtool.driver.ruleslisting every rule ID with its description.results- an array of findings. Each result carriesruleId(the rule ID),level(the severity in SARIF's vocabulary),message.text, andlocations[].physicalLocationwith the file URI and (for line-level findings) aregionwith start and end line and column. Forsecret-assignmentfindings, the result also carries the assignment type asproperties.assignmentType.properties.trestleSummary- the summary fields documented above, when summary is enabled.
When --explain is set, each result also carries message.markdown with the remediation guidance rendered as Markdown.
{
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json",
"version": "2.1.0",
"runs": [{
"tool": {
"driver": {
"name": "trestle",
"rules": [
{ "id": "secret-assignment", "shortDescription": { "text": "..." } },
{ "id": "secret-value", "shortDescription": { "text": "..." } },
{ "id": "binary-secret", "shortDescription": { "text": "..." } },
{ "id": "text-secret", "shortDescription": { "text": "..." } }
]
}
},
"results": [
{
"ruleId": "secret-assignment",
"level": "warning",
"message": { "text": "OpenAI key assigned to variable \"apiKey\"." },
"locations": [{
"physicalLocation": {
"artifactLocation": { "uri": "file:///repo/src/api.ts" },
"region": { "startLine": 3, "startColumn": 18, "endLine": 3, "endColumn": 68 }
}
}],
"properties": { "assignmentType": "variable" }
}
],
"properties": {
"trestleSummary": {
"scannedFileCount": 12,
"elapsedMilliseconds": 42,
"criticalCount": 0,
"warningCount": 1,
"totalCount": 1,
"message": "Scanned 12 files in 42ms. Found 1 warning."
}
}
}]
}xml
A plain XML rendering of the findings, with no external schema. The root element is <trestle>, containing <diagnostics> and (when summary is enabled) <summary>.
The attributes on each <diagnostic> are:
ruleId- the rule ID.severity- the severity.name- the identifier the secret was assigned to. Omitted for rule IDs other thansecret-assignment.assignmentType- the assignment type. Omitted for rule IDs other thansecret-assignment.description- a short label for the credential.file- absolute file path.startLine,startColumn,endLine,endColumn- the position of the match. Omitted forbinary-secretandtext-secret.
The element body holds a <message> child with CDATA. With --explain, an <explanation> child is added alongside the message.
The <summary> element holds one child per summary field. The <message> child wraps its text in CDATA; the other fields hold their value as plain text.
<?xml version="1.0" encoding="UTF-8"?>
<trestle>
<diagnostics>
<diagnostic ruleId="secret-assignment" severity="warning" name="apiKey" assignmentType="variable" description="OpenAI key" file="/repo/src/api.ts" startLine="3" startColumn="18" endLine="3" endColumn="68">
<message><![CDATA[OpenAI key assigned to variable "apiKey".]]></message>
</diagnostic>
</diagnostics>
<summary>
<scannedFileCount>12</scannedFileCount>
<elapsedMilliseconds>42</elapsedMilliseconds>
<criticalCount>0</criticalCount>
<warningCount>1</warningCount>
<totalCount>1</totalCount>
<message><![CDATA[Scanned 12 files in 42ms. Found 1 warning.]]></message>
</summary>
</trestle>Picking a format
textfor interactive runs and pre-commit output.sarifwhen the destination is a code-scanning dashboard or anything that reads SARIF.junitwhen CI is the consumer and the goal is to surface findings as test failures.jsonfor ad-hoc scripts and post-processing.csvfor spreadsheets and quick filtering.xmlfor downstream tools that prefer plain XML over SARIF.
See Command Line for the --output-format, --output-file, and --explain options that control where the formatted output goes and whether it carries remediation guidance.