Skip to content

Commit fbf95c0

Browse files
AlexKantor87claude
andcommitted
docs: add SARIF attestation tutorial
End-to-end walkthrough covering all three customer modes: 1. Raw fact — attest a SARIF file with no policy. Compliance is structural ("a SARIF file was attached"). 2. Two-step rego — run kosli evaluate against the SARIF file, attest both the file and the verdict + policy in a single attestation. 3. One-step rego — pass the .rego file directly to attest; the CLI runs evaluate inline. Uses Checkov as the example scanner since that's the most common customer ask, with an explicit note that any SARIF v2.1.0 producer (Trivy, Semgrep, Snyk, CodeQL, Bandit, ...) works the same way. Added to the "Attesting" tutorial group, listed first. Refs kosli-dev/server#5648 (S5 AC6). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent dec0416 commit fbf95c0

2 files changed

Lines changed: 217 additions & 0 deletions

File tree

config/navigation.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
{
9191
"group": "Attesting",
9292
"pages": [
93+
"tutorials/attest_sarif",
9394
"tutorials/attest_snyk",
9495
"tutorials/custom-attestation-ctrf",
9596
"tutorials/attest_large_documents"

tutorials/attest_sarif.mdx

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
---
2+
title: "Attesting SARIF scans"
3+
description: "Attest the output of any SARIF v2.1.0 security scanner — Checkov, Trivy, Semgrep, Snyk, CodeQL — to a Kosli trail, with optional rego-driven compliance."
4+
---
5+
6+
In this tutorial you'll attest SARIF scan results to a Kosli trail using the `system:sarif` attestation type. You'll work through three modes, each adding one piece on top of the last:
7+
8+
1. **Raw fact** — attest the SARIF file, no policy. Records what the scan found.
9+
2. **Rego policy, two-step** — run a [rego](https://www.openpolicyagent.org/docs/latest/policy-language/) policy with `kosli evaluate`, then attest the SARIF file alongside the policy verdict.
10+
3. **Rego policy, one-step** — pass the `.rego` file directly to `kosli attest`, which runs the policy inline.
11+
12+
By the end you'll have one Kosli trail with three SARIF attestations and a clear sense of which mode fits which situation.
13+
14+
<Note>
15+
This tutorial uses [Checkov](https://www.checkov.io/) to scan a Terraform file, because Checkov is the most common SARIF-producing scanner customers ask us about. Every command after the scan step is identical for any other SARIF v2.1.0 producer — Trivy, Semgrep, Snyk, CodeQL, Bandit. Swap the scanner; the kosli commands don't change.
16+
</Note>
17+
18+
<Steps>
19+
20+
<Step title="Prerequisites">
21+
22+
To follow this tutorial you'll need:
23+
24+
* [Install Checkov](https://www.checkov.io/2.Basics/Installing%20Checkov.html)`pip install checkov`
25+
* [Install Kosli CLI](/getting_started/install)
26+
* [Get a Kosli API token](/getting_started/service-accounts)
27+
28+
Set the environment variables:
29+
30+
```shell
31+
export KOSLI_ORG=<your-personal-kosli-org-name>
32+
export KOSLI_API_TOKEN=<your-api-token>
33+
```
34+
35+
</Step>
36+
37+
<Step title="Create a flow and trail">
38+
39+
Create a flow with a `system:sarif` slot in its template:
40+
41+
```yaml flow_template.yml
42+
version: 1
43+
trail:
44+
attestations:
45+
- name: security-scan
46+
type: system:sarif
47+
```
48+
49+
```shell
50+
kosli create flow sarif-demo \
51+
--template-file flow_template.yml \
52+
--description "Demo of SARIF attestation"
53+
```
54+
55+
You should see: `flow 'sarif-demo' was created`.
56+
57+
Begin a trail to attach attestations to:
58+
59+
```shell
60+
kosli begin trail test-1 --flow sarif-demo
61+
```
62+
63+
You should see: `trail 'test-1' was begun`.
64+
65+
</Step>
66+
67+
<Step title="Get a SARIF file to attest">
68+
69+
Create a small Terraform file with a known misconfiguration so Checkov has something to find:
70+
71+
```hcl main.tf
72+
resource "aws_s3_bucket" "example" {
73+
bucket = "my-tutorial-bucket"
74+
# Intentional findings: no versioning, no encryption, no logging.
75+
}
76+
```
77+
78+
Run Checkov to produce a SARIF file:
79+
80+
```shell
81+
checkov -d . -o sarif --output-file-path . 2>/dev/null
82+
```
83+
84+
This writes `results_sarif.sarif` in the current directory. Inspect the first few lines if you'd like to see the SARIF structure — Kosli will parse it automatically.
85+
86+
</Step>
87+
88+
<Step title="Mode 1: Attest the SARIF file as a raw fact">
89+
90+
The simplest mode: attest the scan results with no compliance policy. The attestation records what Checkov found; compliance is structural only ("a SARIF file was attached").
91+
92+
```shell
93+
kosli attest system sarif \
94+
--flow sarif-demo \
95+
--trail test-1 \
96+
--name security-scan \
97+
--scan-results results_sarif.sarif
98+
```
99+
100+
You should see: `sarif attestation 'security-scan' is reported to trail: test-1`.
101+
102+
Open the trail in the Kosli app (`https://app.kosli.com/<your-org>/flows/sarif-demo/trails/test-1`). The attestation tile shows:
103+
104+
- Tool name and version — pulled dynamically from the SARIF file (so it reads "Checkov 3.2.x", not "Snyk")
105+
- High / medium / low finding counts
106+
- Expandable findings table with rule ID, message, file, and line for each finding
107+
- Compliance: ✓ compliant ("no extra policy evaluated — compliance based on system type JQ rules only")
108+
109+
</Step>
110+
111+
<Step title="Mode 2: Layer in a rego policy (two-step)">
112+
113+
The raw-fact mode is useful but customers usually want compliance gated on the findings — e.g. *"fail if any finding has severity 'error'"* (Checkov's word for high). For that we layer a rego policy on top.
114+
115+
Create a policy file:
116+
117+
```rego checkov.rego
118+
package policy
119+
120+
import rego.v1
121+
122+
default allow = false
123+
124+
violations contains msg if {
125+
some result in input.runs[0].results
126+
result.level == "error"
127+
msg := sprintf("%s in %s:%d", [
128+
result.ruleId,
129+
result.locations[0].physicalLocation.artifactLocation.uri,
130+
result.locations[0].physicalLocation.region.startLine,
131+
])
132+
}
133+
134+
allow if {
135+
count(violations) == 0
136+
}
137+
```
138+
139+
The policy denies the attestation if any Checkov finding has severity `error` (high). Otherwise it allows.
140+
141+
Run the policy against the SARIF file with `kosli evaluate input`:
142+
143+
```shell
144+
kosli evaluate input \
145+
--input-file results_sarif.sarif \
146+
--policy checkov.rego \
147+
--output json > eval.json
148+
```
149+
150+
This writes a JSON file containing `{ "allow": bool, "violations": [...] }`. Take a look — that's the verdict you're about to attest.
151+
152+
Now attest the SARIF file together with the policy verdict and the policy file itself:
153+
154+
```shell
155+
kosli attest system sarif \
156+
--flow sarif-demo \
157+
--trail test-1 \
158+
--name security-scan-with-policy \
159+
--scan-results results_sarif.sarif \
160+
--evaluation-result eval.json \
161+
--policy checkov.rego
162+
```
163+
164+
The `--policy` flag is required whenever you pass `--evaluation-result` — the policy file is uploaded to the audit trail so anyone reading the attestation later can see which rules were applied.
165+
166+
Open the trail again. The new tile now shows:
167+
168+
- The same findings table as mode 1
169+
- A "Policy evaluation" section: verdict badge (allow / deny), violations as readable bullets
170+
- An inline view of the rego policy content (always embedded — no extra click needed to see the rules)
171+
- A download link for the `.rego` file
172+
173+
The compliance verdict now comes from your policy, not from the structural check.
174+
175+
</Step>
176+
177+
<Step title="Mode 3: Same outcome, one command">
178+
179+
Running `kosli evaluate` and `kosli attest system sarif` as two separate commands is fine for CI pipelines that already have an explicit evaluate step. For the common case — "I have a SARIF file and a rego policy, please attest both" — pass the `.rego` file directly to `attest`:
180+
181+
```shell
182+
kosli attest system sarif \
183+
--flow sarif-demo \
184+
--trail test-1 \
185+
--name security-scan-inline \
186+
--scan-results results_sarif.sarif \
187+
--policy checkov.rego
188+
```
189+
190+
The CLI runs `kosli evaluate input` for you, embeds the result, uploads the policy as an attachment, and posts the attestation in one call. The tile in the app looks identical to mode 2.
191+
192+
<Note>
193+
If the policy fails to compile or rego execution errors, the CLI aborts the whole call — no half-attested state. Fix the policy and re-run.
194+
</Note>
195+
196+
</Step>
197+
198+
</Steps>
199+
200+
## What you've accomplished
201+
202+
You now have a Kosli trail with three SARIF attestations covering the three customer modes:
203+
204+
- **`security-scan`** — facts only, no compliance policy
205+
- **`security-scan-with-policy`** — facts plus an explicit rego policy evaluation
206+
- **`security-scan-inline`** — same as above, posted in one CLI call
207+
208+
You've also seen how the SARIF system type's tile renders in each mode, and where the policy file lives in the audit trail.
209+
210+
## Next steps
211+
212+
- **Browse other system types** in the [catalogue](/attestation_types/system/catalogue) — system types is the strategic direction; over time more scanners will get first-class attestation tiles.
213+
- **Read the SARIF type reference** for the full data shape and version history: [System type: SARIF](/attestation_types/system/sarif).
214+
- **Adapt to your scanner** — replace Checkov with Trivy, Semgrep, Snyk, CodeQL, or any other SARIF v2.1.0 producer. The `kosli attest system sarif` commands are unchanged.
215+
- **Migrate from the snyk type** — if you currently use `kosli attest snyk` (even with non-Snyk SARIF), see the migration note on the [SARIF type page](/attestation_types/system/sarif).
216+
- **Wire into CI** — add the same commands to your GitHub Actions pipeline using the [Kosli GitHub Action](/integrations/actions).

0 commit comments

Comments
 (0)