Files
odysseus/docs/security-ci.md

103 lines
4.8 KiB
Markdown
Raw Normal View History

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 12:51:11 -07:00
# 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.