Repository navigation
130 lines (120 loc) · 6.59 KB
/
Copy pathsecurity.yml
File metadata and controls
130 lines (120 loc) · 6.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# The security scans -- configured for a public repo, which means: no tokens (#291).
#
# Why this workflow exists: the pre-launch gate names a gap that has been true since the
# repo opened -- `SONAR_TOKEN` and `SNYK_TOKEN` were names for secrets nobody created, so
# keel had no static analysis or dependency scanning that actually runs. This workflow is
# the tokenless BASELINE: it runs on GITHUB_TOKEN alone, on every pull request and weekly.
# `code-quality.yml` remains the optional enhanced tier (SonarQube + Snyk) for IF those
# tokens are ever created -- its preflight skips cleanly while they are not, so the two
# workflows disagree about nothing: baseline here, optional depth there.
#
# Why these jobs are NOT the `test` job in ci.yml: the `main` ruleset requires the status
# context `test`, which ci.yml produces. A vulnerable dependency or a CodeQL finding is
# information about the state of the world, not a verdict on a proposed change -- a new
# CVE published against an already-pinned version must surface without waiting for a
# merge to blame. So these jobs run alongside (on PRs, and weekly on the calendar, because
# CVEs are published against pinned versions whether or not anyone pushes) without gating
# merges.
name: security
run-name: "Security audit · ${{ github.event_name }} · ${{ github.head_ref || github.ref_name }}"
# ── WHY THIS SKIPS DOCUMENTATION, AND WHY `ci.yml` DELIBERATELY DOES NOT (#644) ──────────────
# This workflow reads no documentation, so a docs-only change can produce no signal from it and
# the run is pure cost. Seven docs-only pull requests merged on 2026-08-30/31 fired it for
# nothing.
#
# ⚠️ DO NOT COPY THIS BLOCK INTO `ci.yml`. keel's documentation is UNDER TEST, on purpose, and
# `ci.yml` is what runs those tests:
#
# * `tests/test_python_floor.py` reads README.md, README.ar.md and docs/desktop-install.md and
# fails when they disagree with the RELEASE's `requires-python`. It exists because five
# places said 3.11 while the wheels required 3.14 (#595/#607).
# * `tests/test_doc_links.py` pins the documented `SHA256SUMS-*` filenames against what
# `release.yml` actually emits -- the docs promised one file, the workflow shipped three
# (#605/#618).
#
# A docs-only pull request is the ONLY kind those pins are for. Filtering docs out of `ci.yml`
# would disable them on exactly the changes they were built to catch.
#
# SAFE HERE for two independent reasons: `main` carries no branch protection, so no check is
# "required" and a skipped run cannot leave a pull request permanently unmergeable; and
# `paths-ignore` applies only to `pull_request` here -- `schedule` fires regardless, so the
# weekly cron below re-scans everything within seven days even on a pathological miss.
#
# `docs/**` sweeps up the experiment drivers and ledgers under `docs/experiments/`. Correct
# here, since this workflow reads none of it; NOT correct for `ci.yml`, where
# `docs/experiments/trials-ledger.jsonl` is a hash-chained artifact the research tests reason
# about.
# ── WHY THERE IS NO `push` TRIGGER ───────────────────────────────────────────────────────────
# Both jobs ran a second time on the merge commit, and the second run cost ~2.5 minutes to
# re-answer a question the pull-request run had answered four minutes earlier: `dependencies`
# audits `uv.lock`, which the merge does not change, and neither job reads anything a merge
# could combine differently. Five merges on 2026-09-01 spent ~12 minutes that way.
#
# ⚠️ THE COST, STATED RATHER THAN DISCOVERED. Code scanning reports alerts against the DEFAULT
# BRANCH, and that baseline is refreshed by analyses OF the default branch -- which now means the
# weekly cron alone. A finding introduced by a merge, or an alert a merge fixes, can therefore
# take up to seven days to appear or clear in the Security tab. Pull-request analysis is
# unaffected and still runs on every change, so nothing reaches `main` unscanned; what is slower
# is the SUMMARY of what is on `main`, not the scanning itself. If that lag ever matters, put
# `push` back rather than shortening the cron -- a daily scan pays most of the cost for a
# fraction of the freshness.
on:
workflow_dispatch:
pull_request:
paths-ignore:
- "docs/**"
- "**/*.md"
- "LICENSE"
schedule:
- cron: "23 4 * * 1"
# Not cancel-in-progress: a cancelled audit run leaves the last completed scan older than
# it needs to be, and these jobs are short enough that waiting beats losing a result.
concurrency:
group: security-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: read
jobs:
dependencies:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v7
- name: Install uv
uses: astral-sh/setup-uv@v7
# Audit the LOCK, not a fresh resolve: `uv export --frozen` emits exactly the pinned
# versions every deployment gets from `uv.lock` as committed (`--frozen` is the same
# never-re-resolve-in-CI stance `code-quality.yml` documents), so those are the
# versions scanned. Unlike the Snyk export there, dev dependencies are INCLUDED
# deliberately: this job gates nothing, so the wider advisory net costs nothing, and
# a CVE in the dev toolchain is still something a contributor's machine runs. Extras
# are included for the same reason; the repo's own eight distributions are excluded
# because they are the code under scan, not third-party dependencies of it.
- name: Export the locked dependency set
run: >
uv export --frozen --all-extras --no-hashes
--no-emit-package keel-trader
--no-emit-package keel-core
--no-emit-package keel-broker-api
--no-emit-package keel-broker-coinbase
--no-emit-package keel-broker-alpaca
--no-emit-package keel-broker-fake
--no-emit-package keel-broker-robinhood
--no-emit-package keel-broker-kraken
> requirements.lock.txt
- name: Audit the locked dependency set
run: uvx pip-audit --requirement requirements.lock.txt
codeql:
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
# CodeQL uploads its findings to the repo's Security tab; without this permission
# the scan runs and its results go nowhere.
security-events: write
contents: read
steps:
- uses: actions/checkout@v7
- uses: github/codeql-action/init@v4
with:
languages: python
- uses: github/codeql-action/analyze@v4