* 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>
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
Dockerfileor 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.
-
Go to the repository on GitHub.
-
Click Settings (top right of the repo).
-
In the left sidebar, click Branches.
-
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;mainis fast-forwarded at releases). -
Enable Require status checks to pass before merging.
-
In the search box that appears, add these checks by name:
Python syntax (compileall)JS syntax (node --check)gitleaksactionlintzizmor (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. -
Also enable Require a pull request before merging and Require review from Code Owners (this uses the
.github/CODEOWNERSfile so every change needs your sign-off). -
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
- Settings -> Code security (or Code security and analysis).
- Turn on Dependency graph (usually on by default for public repos) -- this powers Dependency review and Dependabot.
- Turn on Dependabot alerts and Dependabot security updates.
- Under Code scanning, you have two ways to scan the app code with CodeQL:
- The included
codeql.ymlworkflow already scansmainand 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.
- The included
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.