Exit codes and CI usage
The score-to-verdict mapping, the exit codes, and the GitHub Action.
Score and verdict
The score starts at 100. Each finding subtracts a fixed weight by severity: critical 25, high 10, medium 4, low 1, info 0. The floor is 0. One critical finding is enough to lose a pass; three high findings do it too (two land exactly on the pass line at 80).
| Score | Verdict | Exit code |
|---|---|---|
| 80 to 100 | pass | 0 |
| 50 to 79 | warn | 1 (or 2 with --strict) |
| 0 to 49 | fail | 2 |
Exit code 2 is also used when the score drops below
--fail-under (default 50), even if the verdict
would otherwise be pass or warn. --strict turns a
warn verdict into a fail before the exit code is picked, so warn
always exits 2 under --strict. A .paranoid.toml
at the repo root can set its own fail-under and
strict defaults, used whenever the matching flag is
not itself given; see Team policy file.
Exit code 3 means paranoid itself hit an error: a bad flag, not
a git repository, a git failure, an unreadable file. It never
means a finding fired; the report cannot be trusted either way
when you see a 3. baseline, fix-prompt,
rules, version, and attest
keygen only ever exit 0 on success or 3 on error; they
have no score or verdict of their own to map to 1 or 2.
paranoid attest verify has its own, separate exit
code scheme, since it checks a signature, not a diff: 0 means
the signature is valid, 2 means it is invalid, 3 means
verification could not be attempted at all. See
Signing & attest.
The report always lists which checks ran and which were skipped, with the reason. A pass with skipped checks is still visible in the report, so read the skipped-checks list in CI, not just the exit code.
The GitHub Action
The composite action at alainrk/paranoid/action@main
runs verify on the pull request range, uploads the
report as an artifact, appends it to the job summary, optionally
posts a sticky PR comment, and sets the check status from the
verdict.
permissions:
contents: read
pull-requests: write # only needed for the sticky comment
jobs:
paranoid:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: alainrk/paranoid/action@main
with:
sandbox: none # or auto, when the runner has docker
claims: summary.md # optional
Action inputs
| Input | Default | Meaning |
|---|---|---|
| base | PR base ref (falls back to the PR's base SHA) | State before the agent worked |
| head | PR head | State after the agent worked |
| claims | none | Path to a free-text summary file |
| session | none | Path to an agent session file; ignored when claims is also set |
| sandbox | auto | auto, docker, podman, or none |
| offline | false | Skip registry lookups |
| fail-under | 50 | Fail when the score drops below this |
| strict | false | Treat a warn verdict as failure |
| baseline | none | Path to a baseline file, overriding the auto-detected .paranoid-baseline.json |
| no-baseline | false | Ignore any baseline file, including the auto-detected one |
| version | main | paranoid ref to install with go install |
| comment | true | Post or update a sticky PR comment; set false to skip it and drop the write permission |
| sarif | false | Also write paranoid-report.sarif |
Outputs: verdict (pass, warn, or fail) and score (0 to 100).
The action has no input for .paranoid.toml: it does
not need one, since the file is read from the checked-out repo
itself. A repo's [[disable]] and [sandbox]
keys apply exactly as they do outside CI. Its fail-under
key does not: the action always passes --fail-under
explicitly (this input's own default is 50), so cobra always sees
the flag as set and it always wins over the policy file.
strict is unaffected, since the action only adds
--strict when its own strict input is
true. See Team policy file.
Uploading findings to GitHub code scanning
Set sarif: "true" and add an
upload-sarif step after the action. This needs the
security-events: write permission, which the action
itself never requests.
permissions:
contents: read
pull-requests: write # only needed for the sticky comment
security-events: write # only needed to upload SARIF
jobs:
paranoid:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: alainrk/paranoid/action@main
with:
sandbox: none
sarif: "true"
- uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: paranoid-report.sarif
Findings then show up next to the rest of the repository's code scanning alerts, with the rule's severity, message, and file location; each rule links back to its entry in docs/rules.md. See Report & schema for the SARIF contract itself.
Adopting the Action on an existing repo
Generate and commit .paranoid-baseline.json once,
locally, before turning the Action on. The Action needs no extra
input for this: verify auto-detects the committed
file at the repo root the same way it does outside CI. Use the
baseline input only if you keep the file somewhere
else, and no-baseline: "true" for a workflow that
should ignore it entirely. See
Baseline & suppressions.
Plain CI, no Action
The exit code is enough on any CI system:
paranoid verify --base "origin/$BASE_REF" --format md --output report.md
This repository runs the same check on every push and pull
request; the report of each run is in the job summary.
--format sarif works the same way outside the
Action, for any CI system that accepts a SARIF file (GitHub's
own upload-sarif step, or a third-party code
scanning integration).