Files
odysseus/docs/security-ci.md
T
nopoz 93825a505c ci: security scanning suite and governance (consolidates #305-310) (#1314)
* ci: add security scanning suite and governance

Consolidates the security CI work into one reviewable change. Adds, as
separate workflow files under .github/workflows/:

- secret-scan.yml      gitleaks (pinned + checksum-verified), full history
- workflow-security.yml actionlint + zizmor, audits the workflows themselves
- dependency-review.yml PR dependency gate + advisory pip-audit
- container-scan.yml    hadolint (blocking) + Trivy image scan (advisory)
- codeql.yml            CodeQL for Python and JS, main + weekly

Plus .github/dependabot.yml (pip/npm/actions/docker), .github/CODEOWNERS,
and docs/security-ci.md explaining each check and the one-time settings.

All additive: no existing files are modified. Actions are pinned to commit
SHAs, tokens default-deny (permissions: {}), advisory scans never block,
and SARIF upload is gated to push so fork PRs do not fail on a read-only
token. Composes with the correctness CI in #1015.

* ci(security): isolate Trivy from the Dockerfile lint gate

Address review on #1314 (points 2 and 3).

container-scan.yml now runs only hadolint (the blocking Dockerfile lint)
and keeps the broad pull_request + push:[main] trigger so the required
check always reports and never hangs a PR.

The advisory image scan moves to container-trivy.yml, split by event:
  - pull_request / workflow_dispatch: build and scan under contents:read
    only, no SARIF upload. The image build runs PR-supplied Dockerfile
    instructions, so this path holds no write scope.
  - push to main: build, scan, and upload SARIF with security-events:write.
    Only this trusted path is granted write.
This stops PR jobs from requesting security-events:write they never use,
and a paths-ignore (matching docker-publish.yml) skips the image rebuild
on docs-only changes.

docs/security-ci.md: correct the trigger description to "every pull
request and every push to main", matching the workflows and the existing
ci.yml convention.

Verified locally: zizmor --offline --min-severity=low and actionlint are
clean on the changed and new workflow files.

---------

Co-authored-by: Alexandre Teixeira <111787685+alteixeira20@users.noreply.github.com>
2026-06-11 20:51:11 +01:00

4.8 KiB

Security CI guide

This project runs a set of automated security checks on every pull request and on every push to main. This page explains what each one does, whether it can block a merge, and the few one-time settings you should turn on to get the full benefit.

What runs, and why

Each check lives in its own file under .github/workflows/. They run automatically; you do not start them.

Check What it protects against Blocks a merge?
Secret scan (gitleaks) An API key, token, or password being committed by mistake or on purpose Yes
Workflow security (actionlint + zizmor) A broken or insecure automation file that could leak the repo's access token Yes
Dependency review A pull request that adds a software library with a known security hole Yes
pip-audit Known security holes in the Python libraries already used No (advisory)
Container scan: hadolint Mistakes and insecure patterns in the Dockerfile Yes
Container scan: Trivy Known security holes in the Docker image No (advisory)
CodeQL Real bugs in the app's own code: injection, auth mistakes, path traversal No (advisory)

"Blocks a merge" means a red X appears on the pull request and, once you enable the setting below, the Merge button is disabled until it is fixed.

"Advisory" means it reports problems into the repository's Security tab so you can review them on your own schedule, but it never stops a merge. These are advisory on purpose: they often flag long-standing issues in other people's libraries, not something a given pull request introduced.

Where results appear

  • Checks tab of a pull request: the pass/fail of each check. A green tick is good; a red X needs attention.
  • Security tab of the repository: detailed findings from the advisory scanners (Trivy and CodeQL). This is your dashboard.

If a check fails

  • Secret scan failed: a real credential may have been committed. Treat it as leaked: rotate (regenerate) that key or token immediately, then remove it from the file. Do not just delete the commit; assume it was seen.
  • Dependency review failed: the pull request adds a library with a known vulnerability. Ask the contributor to use a patched version, or decline the change.
  • hadolint / workflow security failed: the contributor changed the Dockerfile or an automation file in a way the linter rejects. Ask them to address the message shown in the failed check.

One-time settings to turn on

These two settings unlock the full value. You only do them once.

1. Require the blocking checks before merging

This makes the Merge button refuse to work until the gating checks pass.

  1. Go to the repository on GitHub.

  2. Click Settings (top right of the repo).

  3. In the left sidebar, click Branches.

  4. Under Branch protection rules, click Add branch ruleset (or Add rule), and set the branch name pattern to dev (this is the branch all pull requests target; main is fast-forwarded at releases).

  5. Enable Require status checks to pass before merging.

  6. In the search box that appears, add these checks by name:

    • Python syntax (compileall)
    • JS syntax (node --check)
    • gitleaks
    • actionlint
    • zizmor (Actions SAST)
    • hadolint (Dockerfile lint)
    • dependency-review (PR gate)

    The first two come from the correctness CI (ci.yml); the rest are this security suite. Leave pytest, pip-audit, Trivy, and CodeQL unchecked so they stay advisory.

  7. Also enable Require a pull request before merging and Require review from Code Owners (this uses the .github/CODEOWNERS file so every change needs your sign-off).

  8. Click Create / Save changes.

Note: a check name only appears in the list after it has run at least once, so let the workflows run on one pull request first, then add them here.

2. Turn on the Security tab features

  1. Settings -> Code security (or Code security and analysis).
  2. Turn on Dependency graph (usually on by default for public repos) -- this powers Dependency review and Dependabot.
  3. Turn on Dependabot alerts and Dependabot security updates.
  4. Under Code scanning, you have two ways to scan the app code with CodeQL:
    • The included codeql.yml workflow already scans main and runs weekly.
    • To also scan pull requests (recommended, since most contributions come from forks), click Set up -> Default under Code scanning. GitHub then runs CodeQL on pull requests for you, with no token limitations.

Keeping it current

.github/dependabot.yml opens small weekly pull requests to update Python and npm packages, the Docker base image, and the pinned automation actions themselves. Review and merge those like any other pull request; they keep the project patched without manual tracking.