Docs · c8 CLI, GitHub Action and Cloud

Run c8, read the margin.

From the first scan to a CI gate and an evidence pack your auditor can re-check. Everything here works without an account; Clause Eight Cloud is optional.

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:

ISO/IEC 27001:2022Annex A
PCI DSS v4.0.1Requirements 1–8 and 10
NIS2Article 21(2)
DORAArt. 9–12 and RTS 2024/1774
Cyber Resilience ActAnnex I
GDPRArt. 5, 9, 25, 30, 32, 87

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/:

FileOpen it when
report.htmlYou want the full picture, grouped by framework and control
report.mdYou want a short summary for a PR or ticket
findings.jsonYou want to feed findings into another tool
results.sarifYou want findings in GitHub code scanning or your IDE
manifest.jsonSomeone asks how the report was produced: commit, versions, the digest of every input and a Merkle root over the findings
statement.jsonThe 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.

CheckPackageScanners
Security bugs in your codeEssentialsSemgrep, Bandit, gosec
Leaked secretsEssentialsgitleaks
Personal data in code and logsEssentialspii-eu
Vulnerable dependenciesEssentialsTrivy
Cloud and container misconfigurationEssentialsCheckov
Unsafe CI pipelinesEssentialszizmor
Risky infrastructure changesInfrastructureplanwhy
Access changes in Terraform plansInfrastructureblastpath
Malicious packagesSupply chain and CRAOSV-Scanner
Software bill of materialsSupply chain and CRASyft
Repository practicesSupply chain and CRAOpenSSF 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.

InputDefaultWhat it does
path.Folder to scan
frameworksallWhich frameworks to report on
fail-onlow, medium, high, critical or none; empty uses [gate]
skipScanners to leave out, e.g. trivy
pluginsFolder with extra scanner plugins you trust
changed-lines-onlytrueOn PRs, count only findings on changed lines
pr-commentstrueGate check, summary and per-line comments
github-tokenworkflow tokenToken for pull request comments and the gate check
scanner-tokenToken that can read the pii-eu, planwhy and blastpath releases
signfalseSign the manifest and statement with Sigstore
upload-sariffalseSend results to GitHub code scanning
upload-reporttrueUpload the report as a workflow artifact
cloud-url, cloud-tokenUse 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

  1. 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.
  2. Hand over the folder, not just the HTML: manifest.json holds the hashes, scanner versions and commit.
  3. Show the accepted-risk register, .c8.toml, alongside it.
  4. 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.
  5. Sign it, optionally: with sign: true and id-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 matches
1Suspicious: an edited file, a bad signature, or a difference nothing explains
2Bad arguments, e.g. the recorded commit isn't in this repository: git fetch it first
3Consistent, but not reproduced exactly, for the causes it names

Between 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 --within days (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 LIST
iso27001,pci,nis2,dora,cra,gdpr, default all
--fail-on SEVERITY
none|low|medium|high|critical
--format FORMAT
text|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.json and .c8.toml
--quiet, --verbose
Less output, or the scanner behind each check

Exit codes

0Success
1Findings at or above --fail-on, only new ones once a baseline exists
2Bad arguments or settings: an unknown framework, a typo in .c8.toml, a broken baseline
3Incomplete scan: a scanner failed, timed out or couldn't install. Reports are still written and name it
4Internal error in Clause Eight; please report it

Privacy 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.toml turns 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_number while the payload says cardNumber passes 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.