| 1 | # Pipeline detail |
| 2 | |
| 3 | ## Triggers |
| 4 | |
| 5 | The workflow runs on push and pull request to `main`, plus `workflow_dispatch` so I |
| 6 | can kick it manually when I'm testing a change to the pipeline itself. |
| 7 | |
| 8 | ## Permissions |
| 9 | |
| 10 | The workflow requests `contents: read` and `security-events: write`. The second is |
| 11 | what lets the SARIF upload write to the Security tab. Nothing in the pipeline needs |
| 12 | write access to the repo contents, so it doesn't ask for it. |
| 13 | |
| 14 | ## Job graph |
| 15 | |
| 16 | `lint` is the fail-fast. If ruff fails there's no point spending runner minutes on |
| 17 | the heavier scans, so everything depends on it. |
| 18 | |
| 19 | `sast`, `secrets`, and `dependencies` have no dependency on each other and run in |
| 20 | parallel. Each one is independently informative: a Semgrep finding, a leaked secret, |
| 21 | and a vulnerable dependency are three different problems with three different fixes. |
| 22 | |
| 23 | `test` waits on all three security jobs. Tests passing on code that has a known |
| 24 | secret leak in it isn't a green pipeline, so the gates come first. |
| 25 | |
| 26 | `notify-soc` uses `if: always()` and depends on `test`, so it reports the outcome |
| 27 | whether the pipeline passed or failed. |
| 28 | |
| 29 | ## Why these tools |
| 30 | |
| 31 | - **ruff** replaces flake8 + isort + a chunk of bandit's lint-level checks in one |
| 32 | fast binary. The `S` rule selection pulls in the security subset. |
| 33 | - **Semgrep** does the real static analysis. Custom rules live in the repo so they |
| 34 | are reviewed like any other code. |
| 35 | - **gitleaks** scans full history on pull requests (`fetch-depth: 0`) so a secret |
| 36 | that was committed and then "removed" in a later commit is still caught. |
| 37 | - **pip-audit** checks the pinned dependencies against the advisory database. `--strict` |
| 38 | makes an unfixable advisory fail the job rather than warn. |
| 39 | |
| 40 | ## Tuning the noise |
| 41 | |
| 42 | Semgrep on `p/default` is broad. The `.gitleaks.toml` allowlist covers the |
| 43 | documented placeholders (`replace-with-...`, `changeme-...`) and the test fixtures so |
| 44 | the secret scan doesn't flag the examples in this repo. Real allowlisting should be |
| 45 | narrow - path- and rule-scoped - which is why the allowlist here is explicit instead |
| 46 | of a blanket ignore. |