Get started
What c8 does
Clause Eight runs open-source scanners you probably know (Semgrep, Checkov, Bandit, gosec, gitleaks, Trivy, zizmor, OSV-Scanner, Syft, OpenSSF Scorecard) and its own, grouped by the problem they find and run only where the repository has something for them. Every finding is sorted into one of 43 categories, and each category cites the controls it weakens:
It writes a report developers can act on and an evidence report an auditor can re-check: every finding carries the commit, scanner version and rule ID, so the same versions on the same commit reproduce it.
Install
c8 needs Python 3.11 or newer. You don't install scanners yourself.
git clone https://github.com/Clause-Eight/clause-eight.git
cd clause-eight
python -m venv .venv && . .venv/bin/activate
pip install .
c8 --version
On the first scan, or ahead of time with c8 setup, c8 installs each scanner at a pinned version into ~/.cache/clause-eight: Python scanners in their own environment each, so their dependencies never clash, and binaries as downloads checked against their published SHA-256. A copy already on your PATH is used instead.
To run c8 from any folder without activating the virtualenv:
ln -s "$PWD/.venv/bin/c8" ~/.local/bin/c8
First scan
Try the demo app first, which has problems planted in every layer, then your own repository:
c8 scan examples/demo-app
c8 scan path/to/your/repo
The terminal shows a summary per framework, then the most severe findings:
Clause Eight: 50 findings to look at (critical 0, high 18, medium 20, low 12)
ISO 27001 21 controls affected A.8.8 (8), A.8.24 (7), A.8.9 (6)
PCI DSS 15 controls affected 6.3.3 (8), 2.2.6 (6), 2.2.1 (5)
NIS2 6 controls affected 21(2)(e) (24), 21(2)(h) (7)
DORA 18 controls affected RTS Art. 16 (16), RTS Art. 10 (11)
CRA 12 controls affected I(2)(e) (9), II(2) (8)
GDPR 5 controls affected Art. 32 (28), Art. 25 (6)
[high ] infra/main.tf:7
Ensure no security groups allow ingress from 0.0.0.0:0 to port 22
ISO 27001 A.8.20; PCI DSS 1.3.1, 1.4.2; NIS2 21(2)(e); DORA Art. 9(4)(b), RTS Art. 13; CRA I(2)(j); GDPR Art. 32
Reports written to c8-report/
A mid-size repository scans in under a minute. The first Trivy run downloads its vulnerability database and takes longer.
Read the results
Everything is written to c8-report/:
| File | Open it when |
|---|---|
report.html | You want the full picture, grouped by framework and control |
report.md | You want a short summary for a PR or ticket |
findings.json | You want to feed findings into another tool |
results.sarif | You want findings in GitHub code scanning or your IDE |
manifest.json | Someone asks how the report was produced: commit, versions, the digest of every input and a Merkle root over the findings |
statement.json | The metadata-only part of the manifest, signed in CI and checked by Cloud |
In the HTML report:
- Each control shows how many findings weaken it. "No findings from these scanners" means the scanners found nothing, not that the control is met.
- Low-confidence mapping means the finding was linked by keywords in the rule title, not a curated rule. Check those first.
- Also: … means a second scanner reported the same problem; it's shown once.
- How this was produced lists the commit, scanner versions, catalog hash, findings root and each rule pack's digest.
Configure
Checks and packages
You choose problems, not tools. Essentials runs by default, and each check runs only where the repository has something for it.
| Check | Package | Scanners |
|---|---|---|
| Security bugs in your code | Essentials | Semgrep, Bandit, gosec |
| Leaked secrets | Essentials | gitleaks |
| Personal data in code and logs | Essentials | pii-eu |
| Vulnerable dependencies | Essentials | Trivy |
| Cloud and container misconfiguration | Essentials | Checkov |
| Unsafe CI pipelines | Essentials | zizmor |
| Risky infrastructure changes | Infrastructure | planwhy |
| Access changes in Terraform plans | Infrastructure | blastpath |
| Malicious packages | Supply chain and CRA | OSV-Scanner |
| Software bill of materials | Supply chain and CRA | Syft |
| Repository practices | Supply chain and CRA | OpenSSF Scorecard, local mode |
Add a package in .c8.toml or in the project's Cloud settings:
[scan]
packages = ["supply-chain", "infrastructure"]
c8 scanners lists the checks, their state and what it suggests for the repository you run it in.
Narrow a scan
c8 scan . --frameworks iso27001,dora # only these frameworks
c8 scan . --skip trivy # leave a scanner out
c8 scan . --offline # bundled rules only, no registry download
c8 scan . --no-default-excludes # include tests, examples, docs, vendor
Reusable Terraform modules
When the repository root is a Terraform module (variables, no provider or backend block, no .tfvars), c8 scans it as a library. Findings on blocks that read var. get the status caller_input: they stay in the reports but don't count and never fail the gate. Hard-coded secrets always count.
[scan]
kind = "library" # or "application"; the default is "auto"
Baselines
An existing codebase usually has many findings. A baseline lets you start from where you are and catch only what's new.
c8 baseline .
git add .c8/baseline.json
git commit -m "chore: clause eight baseline"
c8 scan . --fail-on high # exits 1 only on a NEW high or critical
Code findings match by a hash of the matched lines, so moving code within a file doesn't make them look new. Infrastructure findings match by resource name, dependencies by package. Re-take the baseline after fixing a batch, so the fixed ones drop out.
Only the lines you changed
c8 scan . --changed-since origin/main --fail-on high
Everything is still scanned and reported; findings on untouched lines get outside_diff and don't count.
The gate
--fail-on high fails on findings at or above a severity. For more control, set a gate in .c8.toml:
[gate]
severity = "high" # new findings at or above this fail
frameworks = ["pci", "dora"] # optional: only if they weaken these
block = [ # and these fail whatever their severity
"framework:pci",
"control:iso27001/A.8.24",
"category:secrets",
"rule:CKV_AWS_24",
]
Only findings that still count can fail: new ones once there's a baseline, open ones otherwise, and only changed lines with --changed-since. frameworks narrows severity, never block. --fail-on replaces severity; --fail-on none turns the gate off.
Accept a risk
Sometimes a finding is a conscious decision. Record it with a reason, an owner and an expiry date:
[[accept]]
rule = "CKV_AWS_144" # rule ID, as shown in the report
path = "infra/*.tf" # optional: only in these files
reason = "Single-region by design; daily snapshots cover recovery"
owner = "platform team"
expires = "2027-03-31"
Accepted findings stay in the report, marked accepted with the reason and owner, and don't fail the build. On the expiry date they count again.
Tune a rule
When a rule's verdict doesn't fit a project, tune it, always with a reason. Every tuned finding says so.
[[tune]]
rule = "B101"
tool = "bandit" # optional: only this scanner's rule
action = "off" # counted in manifest.json under tuned_off
reason = "Asserts only run in tests here"
[[tune]]
rule = "CKV_AWS_18"
path = "infra/logging/*"
action = "severity" # or "category"
value = "low"
reason = "Access logs go to the SIEM through the firehose module"
Run in CI
GitHub Actions
The quickest way is c8 ci. A menu in the terminal asks where your CI runs, which frameworks to cite, what fails the build, whether to comment on pull requests and which extra checks to run. It then shows the pipeline and the secrets to add, and writes the file where the platform expects it.
c8 ci # answer the questions
c8 ci --platform gitlab # skip the first one
c8 ci --platform github --fail-on high --defaults # ask nothing
c8 ci --platform bitbucket --print > step.yml # only print it
Or write .github/workflows/clause-eight.yml by hand:
name: clause-eight
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
pull-requests: write # summary and comments on changed lines
checks: write # the Clause Eight gate check
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
- uses: Clause-Eight/clause-eight@<commit-sha>
with:
frameworks: iso27001,pci,nis2,dora,cra
fail-on: high
On pull requests the Action counts only findings on changed lines, leaves one review comment per finding with the controls it weakens and a fix hint, updates them on later pushes and resolves them when the finding goes away. The full report is uploaded as the clause-eight-report artifact.
| Input | Default | What it does |
|---|---|---|
path | . | Folder to scan |
frameworks | all | Which frameworks to report on |
fail-on | low, medium, high, critical or none; empty uses [gate] | |
skip | Scanners to leave out, e.g. trivy | |
plugins | Folder with extra scanner plugins you trust | |
changed-lines-only | true | On PRs, count only findings on changed lines |
pr-comments | true | Gate check, summary and per-line comments |
github-token | workflow token | Token for pull request comments and the gate check |
scanner-token | Token that can read the pii-eu, planwhy and blastpath releases | |
sign | false | Sign the manifest and statement with Sigstore |
upload-sarif | false | Send results to GitHub code scanning |
upload-report | true | Upload the report as a workflow artifact |
cloud-url, cloud-token | Use Cloud settings and upload the results |
GitLab, Bitbucket and others
A container image carries c8 and every scanner at its pinned version, and scans with no network. c8 ci writes the pipeline for each of these. GitLab gets a template that shows findings in the merge request's Code Quality widget (through c8 codequality); Bitbucket gets a pipeline step.
docker run --rm --user "$(id -u):$(id -g)" -v "$PWD:/src" \
ghcr.io/clause-eight/clause-eight:<version> scan /src --offline
Evidence
Prepare for an audit
- Scan the release commit, not a working copy: check out the tag and run
c8 scan ., or use the report artifact from that commit's CI run. - Hand over the folder, not just the HTML:
manifest.jsonholds the hashes, scanner versions and commit. - Show the accepted-risk register,
.c8.toml, alongside it. - Pack it into one zip with every file, the signature and a README for the auditor. The same evidence always gives the same zip, so you can quote its SHA-256.
- Sign it, optionally: with
sign: trueandid-token: write, the Action signs the manifest and statement with Sigstore as the workflow that ran the scan.
c8 evidence --report c8-report --out evidence.zip
Verify a report
c8 verify --report c8-report # hashes, Merkle root, then a re-run
c8 verify --report c8-report --hashes-only # skip the re-run
c8 verify --report c8-report --offline # re-run without fetching rule packs
c8 verify --report c8-report --finding <id> # prove and re-check one finding
Verify checks every file against manifest.json and the findings against its Merkle root, then re-runs the scan at the recorded commit in a temporary checkout. Each difference is named by its cause: source, scanner, rule pack, config or catalog mapping changed.
0Everything matches1Suspicious: an edited file, a bad signature, or a difference nothing explains2Bad arguments, e.g. the recorded commit isn't in this repository: git fetch it first3Consistent, but not reproduced exactly, for the causes it namesBetween audits
Keep each audit's findings.json and pass the last one to the next scan. The reports then say how many findings opened and which were fixed since, per control.
c8 scan . --previous evidence/2026-Q3/findings.json --out evidence/2026-Q4
Cloud
Connect to Clause Eight Cloud
Cloud adds what needs a server: history per repository and control, an auditor portal, verified evidence and curated mappings.
c8 login --url https://<your Cloud address> # hidden prompt for the token
c8 upload --dry-run upload.json # see exactly what would be sent
c8 upload
In CI, set C8_TOKEN (a project token from the project's Tokens page) and C8_CLOUD_URL instead of logging in. Uploads carry the branch, pull request and CI run link, so pull request scans don't move the project's history.
Settings from Cloud
A project's settings page holds its frameworks, gate, excluded paths, accepted risks and tuning, versioned with who changed what and why. Scan with --cloud-settings (or C8_CLOUD_SETTINGS=1) to combine them with the repository's .c8.toml:
- the project's frameworks, gate and tuning win;
- excluded paths and accepted risks from both apply.
Reference
Commands
c8 help [command] shows every option with examples.
c8 scan [path]- Scan a folder and write reports
c8 baseline [path]- Record current findings so later scans report only new ones
c8 scanners [list|new NAME]- List scanners and their state, or create a plugin template
c8 setup [--only LIST]- Install missing scanners now
c8 controls [--framework NAME]- List every control and what checks it
c8 comment- Post or preview pull request comments for a scan
c8 ci- Build a CI pipeline for GitHub, GitLab, Bitbucket or any CI from a menu
c8 codequality- Write the findings as a GitLab Code Quality report
c8 risks- List accepted risks, expired first, then those expiring within
--withindays (default 30) c8 evidence- Pack a report into one reproducible zip
c8 verify- Check a report's hashes and re-run its scan
c8 login,c8 logout- Store or remove the Cloud address and token
c8 upload- Send a scan's findings metadata to Cloud
c8 scan options
--out DIR- Output folder, default
c8-report --frameworks LISTiso27001,pci,nis2,dora,cra,gdpr, default all--fail-on SEVERITYnone|low|medium|high|critical--format FORMATtext|json|markdown|sarif--skip LIST,--only LIST- Leave scanners out, or run only these
--plugins DIR- Extra folder with scanner plugins, repeatable
--no-install- Don't install missing scanners
--offline- Only bundled Semgrep rules
--semgrep-config CONFIG- Semgrep rule set, repeatable, default
p/default --no-semantic- Skip semantic checks this run
--cloud-settings- Merge the project's settings and tuning from Cloud
--changed-since REF- Count only findings on lines changed since REF
--previous FILE- An earlier
findings.json: report what opened and what was fixed since --baseline FILE,--config FILE- Defaults
.c8/baseline.jsonand.c8.toml --quiet,--verbose- Less output, or the scanner behind each check
Exit codes
0Success1Findings at or above --fail-on, only new ones once a baseline exists2Bad arguments or settings: an unknown framework, a typo in .c8.toml, a broken baseline3Incomplete scan: a scanner failed, timed out or couldn't install. Reports are still written and name it4Internal error in Clause Eight; please report itPrivacy and security
- Everything runs on your machine or CI runner, and c8 sends nothing anywhere. Semgrep registry rules come from semgrep.dev, cached by SHA-256, unless you pass
--offline; Semgrep metrics are off. - gitleaks runs with
--redact; only the rule, file and line of a secret are kept. - Reports hold file paths, line numbers, rule IDs and scanner messages, not your code.
- Git remote URLs are stripped of credentials before they go into the manifest.
- Semantic checks are the one exception, and only when
.c8.tomlturns them on: a few redacted lines go to the model you choose, which can run on your own machine.
Known limits
- Logic bugs patterns can't see. A sanitizer that masks
card_numberwhile the payload sayscardNumberpasses every pattern rule. - Checkov gives no severity without a Prisma Cloud key; c8 uses a default per category and says so.
- Mapping coverage. Checks outside the curated list fall back to keywords and are marked low confidence.
- Organisational controls such as policies, training, supplier contracts and incident response are out of scope by design.
Troubleshooting
"No scanner could run"
Everything was skipped, missing with installs off, or failed to install. c8 scanners shows each one's state; c8 setup installs what's missing and prints why anything fails.
A scanner is listed under "Skipped"
The report shows why: not installed with installs off, install failed, or skipped by option. Run c8 setup to see install errors.
A download fails behind a proxy
c8 uses the standard HTTPS_PROXY setting. If downloads aren't possible, install gitleaks and Trivy yourself; c8 uses any copy on your PATH.
A finding is mapped to the wrong control
The mapping lives in src/clause_eight/catalog/rules.toml. Exact rule IDs win over CWE IDs, which win over keywords. Open an issue with the rule ID.
Lots of findings in example or test Terraform
Those folders are excluded by default. If yours live elsewhere, add them to exclude in .c8.toml.
Old findings show up as new after a refactor
Moving code within a file is fine; renaming a file or Terraform resource changes its identity. Re-take the baseline.