github-security-report 0.18.1__tar.gz → 0.19.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {github_security_report-0.18.1 → github_security_report-0.19.0}/PKG-INFO +86 -20
- {github_security_report-0.18.1 → github_security_report-0.19.0}/README.md +85 -19
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/_version.py +2 -2
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/authors.py +31 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/categories/keys.py +4 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/categories/tables.py +21 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/cli/app.py +9 -6
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/cli/modes.py +27 -12
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/cli/options.py +3 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/cli/serialise.py +3 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/client/org_reads.py +71 -47
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/client/parsers.py +2 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/client/queries.py +12 -3
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/client/reads.py +20 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/client/writes.py +32 -0
- github_security_report-0.19.0/src/github_security_report/code_quality.py +164 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/collect/extras.py +88 -43
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/collect/org.py +13 -1
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/collect/protocols.py +11 -4
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/models.py +4 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/posture/__init__.py +10 -3
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/posture/facts.py +8 -0
- github_security_report-0.19.0/src/github_security_report/posture/quality.py +96 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/remediate/__init__.py +42 -3
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/remediate/model.py +7 -1
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/render/terminal.py +12 -0
- github_security_report-0.19.0/src/github_security_report/render/terminal_notices.py +48 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/report/aggregate.py +14 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/report/tables.py +13 -2
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/summary.py +22 -6
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/templates/report.html.j2 +2 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/.gitignore +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/LICENSE +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/LICENSES/Apache-2.0.txt +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/pyproject.toml +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/scripts/README.md +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/__init__.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/categories/__init__.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/categories/signals.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/classify.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/cli/__init__.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/cli/__main__.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/cli/boundary.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/cli/outputs.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/cli/publish.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/client/__init__.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/client/accounting.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/client/alerts.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/client/batch_errors.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/client/codeql_parsers.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/client/copilot.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/client/endpoints.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/client/errors.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/client/failures.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/client/reviews.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/client/transport.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/codeql/__init__.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/codeql/cleanup.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/codeql/facts.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/codeql/tables.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/collect/__init__.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/collect/codeql.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/collect/context.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/collect/graph.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/collect/repo.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/collect/scoping.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/config/__init__.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/config/loader.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/config/models.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/config/order.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/config/schema.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/gating.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/gitctx.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/issues.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/layout.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/ordering.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/posture/enablement.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/posture/releases.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/pulls/__init__.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/pulls/columns.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/pulls/counting.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/pulls/presentation.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/pulls/table.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/py.typed +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/ranking.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/remediate/codeql.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/render/__init__.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/render/html.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/render/markdown.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/render/slack.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/render/slack_limits.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/report/__init__.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/report/display.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/report/signals.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/rulesets.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/runner.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/scope.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/secret_patterns.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/severity.py +0 -0
- {github_security_report-0.18.1 → github_security_report-0.19.0}/src/github_security_report/templates/index.html.j2 +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: github-security-report
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.19.0
|
|
4
4
|
Summary: Security and quality reporting across GitHub organisations
|
|
5
5
|
Project-URL: Homepage, https://github.com/lfreleng-actions/github-security-report-action
|
|
6
6
|
Project-URL: Repository, https://github.com/lfreleng-actions/github-security-report-action
|
|
@@ -76,7 +76,9 @@ states and rendered worst-first:
|
|
|
76
76
|
|
|
77
77
|
Every category renders the same **standardised summary footer** beneath its
|
|
78
78
|
table: remediation-first count lines (failures, disabled, unknown, then the
|
|
79
|
-
healthy pass line, then excluded).
|
|
79
|
+
healthy pass line, then excluded). A category may add a note qualifying the
|
|
80
|
+
pass line (Code Quality's per-language breakdown) and a **Not applicable**
|
|
81
|
+
count, both shown on every surface. The pass line reads **"All <state>"** when
|
|
80
82
|
nothing needs attention, or **"N <state>"** otherwise. The terminal and Slack
|
|
81
83
|
stay brevity-first; the explanatory per-category description and documentation
|
|
82
84
|
link are shown only on the richer Markdown and HTML (GitHub Pages) outputs.
|
|
@@ -154,6 +156,26 @@ tables (org mode):
|
|
|
154
156
|
window a reviewed dependency update sits in while the vulnerability it fixes
|
|
155
157
|
stays unpatched. Read from the batched GraphQL prefetch, so it costs no extra
|
|
156
158
|
request; hide it with the `auto_merge` render toggle.
|
|
159
|
+
- **Code Quality** — repositories where
|
|
160
|
+
[GitHub Code Quality](https://docs.github.com/en/code-security/concepts/code-quality/code-quality)
|
|
161
|
+
is **Disabled**, or **Partial**: configured, but without a language the
|
|
162
|
+
repository holds. Code Quality analyses six language families, so each
|
|
163
|
+
repository is first classified from its GitHub Linguist languages, read
|
|
164
|
+
from the batched GraphQL prefetch: Python (`python`), Go (`go`), Java and
|
|
165
|
+
Kotlin (`java-kotlin`), JavaScript and TypeScript
|
|
166
|
+
(`javascript-typescript`), Ruby (`ruby`) and C# (`csharp`). A repository
|
|
167
|
+
with none of them, such as one holding only shell scripts and workflow
|
|
168
|
+
files, is **Not applicable**: counted on a line of its own and never
|
|
169
|
+
reported as off. Only applicable repositories have their setup read, with
|
|
170
|
+
`GET /repos/{owner}/{repo}/code-quality/setup`; one whose setup or languages
|
|
171
|
+
cannot be read, or that holds more than the twenty languages the prefetch
|
|
172
|
+
reads, is counted as **Unreadable**, never as Disabled. The
|
|
173
|
+
**Missing** column names the languages each listed repository still needs,
|
|
174
|
+
and a footer line on every surface, the terminal included, gives the
|
|
175
|
+
enabled count out of the applicable count per language, such as
|
|
176
|
+
`python 30/32 · go 5/5 · javascript-typescript 10/12`. See
|
|
177
|
+
[ADR-0006](docs/adr/0006-github-code-quality.md); hide it with the
|
|
178
|
+
`code_quality` render toggle.
|
|
157
179
|
|
|
158
180
|
The four enablement categories count the repositories with and without the
|
|
159
181
|
feature (plus any whose state could not be read), and by default name whichever
|
|
@@ -192,7 +214,13 @@ signal is skipped and every other part of the report is unaffected, so the
|
|
|
192
214
|
minimal tokens below omit it. Grant the org-admin permission only if you want
|
|
193
215
|
ruleset-based tool coverage.
|
|
194
216
|
|
|
195
|
-
|
|
217
|
+
GitHub Code Quality is gated the same way: a fine-grained token needs
|
|
218
|
+
repository Administration **write** to read a repository's Code Quality setup,
|
|
219
|
+
even though the report only reads it. Without it every repository Code Quality
|
|
220
|
+
applies to is reported as **Unreadable** in that one category; a classic PAT's
|
|
221
|
+
`repo` scope already covers the read.
|
|
222
|
+
|
|
223
|
+
A token without the org-admin permission gets a `404` from the endpoint, which the tool
|
|
196
224
|
reports at **INFO** — so it is invisible unless you pass `--verbose`:
|
|
197
225
|
|
|
198
226
|
```text
|
|
@@ -212,7 +240,7 @@ A fine-grained PAT is bound to one resource owner, so it works for a report
|
|
|
212
240
|
covering a **single** organisation. Create it with **Resource owner** set to the
|
|
213
241
|
organisation and **Repository access** set to *All repositories*, then grant:
|
|
214
242
|
|
|
215
|
-
**Repository permissions** (
|
|
243
|
+
**Repository permissions** (Read-only unless noted):
|
|
216
244
|
|
|
217
245
|
| Permission | Used for |
|
|
218
246
|
| ---------- | -------- |
|
|
@@ -222,7 +250,7 @@ organisation and **Repository access** set to *All repositories*, then grant:
|
|
|
222
250
|
| Code scanning alerts | CodeQL / Scorecard / zizmor / aislop findings |
|
|
223
251
|
| Secret scanning alerts | Open secret-scanning alerts, across every GitHub pattern category |
|
|
224
252
|
| Issues | Open issues and their labels (GitHub Issues table) |
|
|
225
|
-
| Administration | Dependabot enablement + security-updates status, and effective branch rules |
|
|
253
|
+
| Administration | Dependabot enablement + security-updates status, and effective branch rules. *Optional* **write** reads the Code Quality setup, which GitHub gates behind write |
|
|
226
254
|
| Actions | Workflow state behind a stale CodeQL configuration |
|
|
227
255
|
|
|
228
256
|
**Organization permissions:**
|
|
@@ -232,7 +260,7 @@ organisation and **Repository access** set to *All repositories*, then grant:
|
|
|
232
260
|
| Administration | Read (write for rulesets) | *Optional*, and the only permission here with a write variant. **Read** keeps the token read-only and unlocks GitHub's secret-scanning pattern inventory (see below). **Write** additionally unlocks organisation rulesets (detecting tools enforced through an org ruleset), which GitHub gates behind Administration write. Omit the permission entirely to skip both; everything else is unaffected. |
|
|
233
261
|
|
|
234
262
|
> Read-only is enough for everything except the optional ruleset coverage
|
|
235
|
-
> above. A fine-grained token cannot span organisations. For a report covering
|
|
263
|
+
> and the Code Quality setup read above. A fine-grained token cannot span organisations. For a report covering
|
|
236
264
|
> more than one org, use a classic PAT (below).
|
|
237
265
|
|
|
238
266
|
### Multiple organisations — classic PAT
|
|
@@ -464,10 +492,11 @@ Pages output. The valid category keys are: `codeql`,
|
|
|
464
492
|
`zizmor`, `aislop`, `dependabot_alerts`, `secret_scanning`,
|
|
465
493
|
`dependabot_alerts_enabled`, `dependabot_updates_enabled`,
|
|
466
494
|
`dependabot_cooldown`, `releases`, `mutable_releases`,
|
|
467
|
-
`private_vulnerability_reporting`, `auto_merge`, `
|
|
468
|
-
`pull_requests`, `pull_requests_assigned`. Like the other
|
|
469
|
-
`categories` can be set globally and overridden per
|
|
470
|
-
merge key-by-key, so flipping one output leaves the
|
|
495
|
+
`private_vulnerability_reporting`, `auto_merge`, `code_quality`,
|
|
496
|
+
`github_issues`, `pull_requests`, `pull_requests_assigned`. Like the other
|
|
497
|
+
`report` settings, `categories` can be set globally and overridden per
|
|
498
|
+
organisation (overrides merge key-by-key, so flipping one output leaves the
|
|
499
|
+
rest untouched). The
|
|
471
500
|
machine-readable `report.json` artifact always contains the complete dataset,
|
|
472
501
|
regardless of these toggles.
|
|
473
502
|
|
|
@@ -1281,10 +1310,13 @@ see the relationship reports it), but one naming an outsider proves nothing,
|
|
|
1281
1310
|
because that is exactly how a private member appears to an under-privileged
|
|
1282
1311
|
token. Those authors are reported as indeterminate rather than external, so
|
|
1283
1312
|
`Ext` **undercounts and should be read as a lower bound** — the table says so in
|
|
1284
|
-
its description whenever this happens, and the
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1313
|
+
its description whenever this happens, and the terminal report prints one
|
|
1314
|
+
warning line under each affected organisation's heading, naming the cause: a
|
|
1315
|
+
token without the membership permission, a token whose account is not a member,
|
|
1316
|
+
or a read that failed. Membership is never used partially: a failed page or an
|
|
1317
|
+
organisation beyond the pagination guard is treated as unreadable rather than as
|
|
1318
|
+
a member list with people missing from it, since every absent member would
|
|
1319
|
+
otherwise read as an outsider.
|
|
1288
1320
|
|
|
1289
1321
|
**Automation is never counted as external.** Bots are outsiders by association —
|
|
1290
1322
|
`dependabot[bot]` genuinely reports `CONTRIBUTOR` or `NONE` — so counting on
|
|
@@ -1297,8 +1329,9 @@ association value GitHub has newly introduced — is **not** counted as external
|
|
|
1297
1329
|
The column understates rather than inventing an outsider.
|
|
1298
1330
|
|
|
1299
1331
|
**Permissions.** Reading organisation membership needs `read:org` (classic) or
|
|
1300
|
-
**Members: read** (fine-grained). Without it the
|
|
1301
|
-
|
|
1332
|
+
**Members: read** (fine-grained). Without it the terminal report prints
|
|
1333
|
+
`⚠️ Token lacks read/organisation permission; results incomplete for <org>`
|
|
1334
|
+
and reports externally-raised counts as a lower bound rather than guessing from
|
|
1302
1335
|
`authorAssociation` alone.
|
|
1303
1336
|
|
|
1304
1337
|
### Organisation feature gating
|
|
@@ -1648,8 +1681,8 @@ the run, rather than falling back to every repository. The run then collects
|
|
|
1648
1681
|
exactly the repositories that check validated, without listing the
|
|
1649
1682
|
organisation again, and prints `Limited to:` beneath its heading.
|
|
1650
1683
|
|
|
1651
|
-
The remediable categories are the
|
|
1652
|
-
|
|
1684
|
+
The remediable categories are the features with a documented enablement
|
|
1685
|
+
endpoint:
|
|
1653
1686
|
|
|
1654
1687
|
| `--category` | Enables |
|
|
1655
1688
|
|---|---|
|
|
@@ -1659,15 +1692,48 @@ enablement endpoint:
|
|
|
1659
1692
|
| `dependabot_updates_enabled` | Dependabot security updates (plus alerts) |
|
|
1660
1693
|
| `private_vulnerability_reporting` | Private vulnerability reporting |
|
|
1661
1694
|
| `auto_merge` | The repository's "Allow auto-merge" setting |
|
|
1695
|
+
| `code_quality` | GitHub Code Quality, per language (named only; billed) |
|
|
1696
|
+
| `codeql_stale_configurations` | Stale CodeQL cleanup (named only; deletes) |
|
|
1662
1697
|
|
|
1663
1698
|
Qualitative findings (Scorecard, zizmor, open Dependabot alerts, cooldown,
|
|
1664
1699
|
release freshness/mutability) are reported but not auto-remediated. Remediation
|
|
1665
1700
|
is organisation-scoped (`--scope org`, the default and only supported scope).
|
|
1666
1701
|
|
|
1702
|
+
The last two run **only when named** with `--category`, and the no-argument
|
|
1703
|
+
run never includes them: `code_quality`, because GitHub bills it,
|
|
1704
|
+
and `codeql_stale_configurations`, because it deletes data.
|
|
1705
|
+
|
|
1706
|
+
### Enabling GitHub Code Quality
|
|
1707
|
+
|
|
1708
|
+
GitHub Code Quality is billed per active committer and uses GitHub Actions
|
|
1709
|
+
minutes, so switching it on is a spending decision a routine run must not
|
|
1710
|
+
make:
|
|
1711
|
+
|
|
1712
|
+
```bash
|
|
1713
|
+
# Preview: each repository with the languages it would get.
|
|
1714
|
+
uvx github-security-report remediate --org lfreleng-actions \
|
|
1715
|
+
--category code_quality
|
|
1716
|
+
|
|
1717
|
+
# Apply.
|
|
1718
|
+
uvx github-security-report remediate --org lfreleng-actions \
|
|
1719
|
+
--category code_quality --apply
|
|
1720
|
+
```
|
|
1721
|
+
|
|
1722
|
+
It acts on the rows of **Code Quality**, the Disabled and Partial
|
|
1723
|
+
repositories. Each one is configured for the union of the languages its
|
|
1724
|
+
setup already analyses and the ones its Linguist languages make applicable,
|
|
1725
|
+
so a write never drops a configured language; the dry run names them, as in
|
|
1726
|
+
`dependamerge (python, javascript-typescript)`. Not applicable and Unreadable
|
|
1727
|
+
repositories are never written. GitHub provisions the first analysis
|
|
1728
|
+
asynchronously. A `409` (an update already in progress), a `403` (an archived
|
|
1729
|
+
repository, or Code Quality not allowed for it) or a `422` fails that
|
|
1730
|
+
repository only and names the cause. The token needs the classic `repo`
|
|
1731
|
+
scope, or a fine-grained token's Administration: write. See
|
|
1732
|
+
[ADR-0006](docs/adr/0006-github-code-quality.md) for the reasoning.
|
|
1733
|
+
|
|
1667
1734
|
### Cleaning up stale CodeQL configurations
|
|
1668
1735
|
|
|
1669
|
-
|
|
1670
|
-
run never includes it:
|
|
1736
|
+
This category is **destructive**, so it runs only when named:
|
|
1671
1737
|
|
|
1672
1738
|
```bash
|
|
1673
1739
|
# Preview: which configurations would go, and which are refused and why.
|
|
@@ -32,7 +32,9 @@ states and rendered worst-first:
|
|
|
32
32
|
|
|
33
33
|
Every category renders the same **standardised summary footer** beneath its
|
|
34
34
|
table: remediation-first count lines (failures, disabled, unknown, then the
|
|
35
|
-
healthy pass line, then excluded).
|
|
35
|
+
healthy pass line, then excluded). A category may add a note qualifying the
|
|
36
|
+
pass line (Code Quality's per-language breakdown) and a **Not applicable**
|
|
37
|
+
count, both shown on every surface. The pass line reads **"All <state>"** when
|
|
36
38
|
nothing needs attention, or **"N <state>"** otherwise. The terminal and Slack
|
|
37
39
|
stay brevity-first; the explanatory per-category description and documentation
|
|
38
40
|
link are shown only on the richer Markdown and HTML (GitHub Pages) outputs.
|
|
@@ -110,6 +112,26 @@ tables (org mode):
|
|
|
110
112
|
window a reviewed dependency update sits in while the vulnerability it fixes
|
|
111
113
|
stays unpatched. Read from the batched GraphQL prefetch, so it costs no extra
|
|
112
114
|
request; hide it with the `auto_merge` render toggle.
|
|
115
|
+
- **Code Quality** — repositories where
|
|
116
|
+
[GitHub Code Quality](https://docs.github.com/en/code-security/concepts/code-quality/code-quality)
|
|
117
|
+
is **Disabled**, or **Partial**: configured, but without a language the
|
|
118
|
+
repository holds. Code Quality analyses six language families, so each
|
|
119
|
+
repository is first classified from its GitHub Linguist languages, read
|
|
120
|
+
from the batched GraphQL prefetch: Python (`python`), Go (`go`), Java and
|
|
121
|
+
Kotlin (`java-kotlin`), JavaScript and TypeScript
|
|
122
|
+
(`javascript-typescript`), Ruby (`ruby`) and C# (`csharp`). A repository
|
|
123
|
+
with none of them, such as one holding only shell scripts and workflow
|
|
124
|
+
files, is **Not applicable**: counted on a line of its own and never
|
|
125
|
+
reported as off. Only applicable repositories have their setup read, with
|
|
126
|
+
`GET /repos/{owner}/{repo}/code-quality/setup`; one whose setup or languages
|
|
127
|
+
cannot be read, or that holds more than the twenty languages the prefetch
|
|
128
|
+
reads, is counted as **Unreadable**, never as Disabled. The
|
|
129
|
+
**Missing** column names the languages each listed repository still needs,
|
|
130
|
+
and a footer line on every surface, the terminal included, gives the
|
|
131
|
+
enabled count out of the applicable count per language, such as
|
|
132
|
+
`python 30/32 · go 5/5 · javascript-typescript 10/12`. See
|
|
133
|
+
[ADR-0006](docs/adr/0006-github-code-quality.md); hide it with the
|
|
134
|
+
`code_quality` render toggle.
|
|
113
135
|
|
|
114
136
|
The four enablement categories count the repositories with and without the
|
|
115
137
|
feature (plus any whose state could not be read), and by default name whichever
|
|
@@ -148,7 +170,13 @@ signal is skipped and every other part of the report is unaffected, so the
|
|
|
148
170
|
minimal tokens below omit it. Grant the org-admin permission only if you want
|
|
149
171
|
ruleset-based tool coverage.
|
|
150
172
|
|
|
151
|
-
|
|
173
|
+
GitHub Code Quality is gated the same way: a fine-grained token needs
|
|
174
|
+
repository Administration **write** to read a repository's Code Quality setup,
|
|
175
|
+
even though the report only reads it. Without it every repository Code Quality
|
|
176
|
+
applies to is reported as **Unreadable** in that one category; a classic PAT's
|
|
177
|
+
`repo` scope already covers the read.
|
|
178
|
+
|
|
179
|
+
A token without the org-admin permission gets a `404` from the endpoint, which the tool
|
|
152
180
|
reports at **INFO** — so it is invisible unless you pass `--verbose`:
|
|
153
181
|
|
|
154
182
|
```text
|
|
@@ -168,7 +196,7 @@ A fine-grained PAT is bound to one resource owner, so it works for a report
|
|
|
168
196
|
covering a **single** organisation. Create it with **Resource owner** set to the
|
|
169
197
|
organisation and **Repository access** set to *All repositories*, then grant:
|
|
170
198
|
|
|
171
|
-
**Repository permissions** (
|
|
199
|
+
**Repository permissions** (Read-only unless noted):
|
|
172
200
|
|
|
173
201
|
| Permission | Used for |
|
|
174
202
|
| ---------- | -------- |
|
|
@@ -178,7 +206,7 @@ organisation and **Repository access** set to *All repositories*, then grant:
|
|
|
178
206
|
| Code scanning alerts | CodeQL / Scorecard / zizmor / aislop findings |
|
|
179
207
|
| Secret scanning alerts | Open secret-scanning alerts, across every GitHub pattern category |
|
|
180
208
|
| Issues | Open issues and their labels (GitHub Issues table) |
|
|
181
|
-
| Administration | Dependabot enablement + security-updates status, and effective branch rules |
|
|
209
|
+
| Administration | Dependabot enablement + security-updates status, and effective branch rules. *Optional* **write** reads the Code Quality setup, which GitHub gates behind write |
|
|
182
210
|
| Actions | Workflow state behind a stale CodeQL configuration |
|
|
183
211
|
|
|
184
212
|
**Organization permissions:**
|
|
@@ -188,7 +216,7 @@ organisation and **Repository access** set to *All repositories*, then grant:
|
|
|
188
216
|
| Administration | Read (write for rulesets) | *Optional*, and the only permission here with a write variant. **Read** keeps the token read-only and unlocks GitHub's secret-scanning pattern inventory (see below). **Write** additionally unlocks organisation rulesets (detecting tools enforced through an org ruleset), which GitHub gates behind Administration write. Omit the permission entirely to skip both; everything else is unaffected. |
|
|
189
217
|
|
|
190
218
|
> Read-only is enough for everything except the optional ruleset coverage
|
|
191
|
-
> above. A fine-grained token cannot span organisations. For a report covering
|
|
219
|
+
> and the Code Quality setup read above. A fine-grained token cannot span organisations. For a report covering
|
|
192
220
|
> more than one org, use a classic PAT (below).
|
|
193
221
|
|
|
194
222
|
### Multiple organisations — classic PAT
|
|
@@ -420,10 +448,11 @@ Pages output. The valid category keys are: `codeql`,
|
|
|
420
448
|
`zizmor`, `aislop`, `dependabot_alerts`, `secret_scanning`,
|
|
421
449
|
`dependabot_alerts_enabled`, `dependabot_updates_enabled`,
|
|
422
450
|
`dependabot_cooldown`, `releases`, `mutable_releases`,
|
|
423
|
-
`private_vulnerability_reporting`, `auto_merge`, `
|
|
424
|
-
`pull_requests`, `pull_requests_assigned`. Like the other
|
|
425
|
-
`categories` can be set globally and overridden per
|
|
426
|
-
merge key-by-key, so flipping one output leaves the
|
|
451
|
+
`private_vulnerability_reporting`, `auto_merge`, `code_quality`,
|
|
452
|
+
`github_issues`, `pull_requests`, `pull_requests_assigned`. Like the other
|
|
453
|
+
`report` settings, `categories` can be set globally and overridden per
|
|
454
|
+
organisation (overrides merge key-by-key, so flipping one output leaves the
|
|
455
|
+
rest untouched). The
|
|
427
456
|
machine-readable `report.json` artifact always contains the complete dataset,
|
|
428
457
|
regardless of these toggles.
|
|
429
458
|
|
|
@@ -1237,10 +1266,13 @@ see the relationship reports it), but one naming an outsider proves nothing,
|
|
|
1237
1266
|
because that is exactly how a private member appears to an under-privileged
|
|
1238
1267
|
token. Those authors are reported as indeterminate rather than external, so
|
|
1239
1268
|
`Ext` **undercounts and should be read as a lower bound** — the table says so in
|
|
1240
|
-
its description whenever this happens, and the
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1269
|
+
its description whenever this happens, and the terminal report prints one
|
|
1270
|
+
warning line under each affected organisation's heading, naming the cause: a
|
|
1271
|
+
token without the membership permission, a token whose account is not a member,
|
|
1272
|
+
or a read that failed. Membership is never used partially: a failed page or an
|
|
1273
|
+
organisation beyond the pagination guard is treated as unreadable rather than as
|
|
1274
|
+
a member list with people missing from it, since every absent member would
|
|
1275
|
+
otherwise read as an outsider.
|
|
1244
1276
|
|
|
1245
1277
|
**Automation is never counted as external.** Bots are outsiders by association —
|
|
1246
1278
|
`dependabot[bot]` genuinely reports `CONTRIBUTOR` or `NONE` — so counting on
|
|
@@ -1253,8 +1285,9 @@ association value GitHub has newly introduced — is **not** counted as external
|
|
|
1253
1285
|
The column understates rather than inventing an outsider.
|
|
1254
1286
|
|
|
1255
1287
|
**Permissions.** Reading organisation membership needs `read:org` (classic) or
|
|
1256
|
-
**Members: read** (fine-grained). Without it the
|
|
1257
|
-
|
|
1288
|
+
**Members: read** (fine-grained). Without it the terminal report prints
|
|
1289
|
+
`⚠️ Token lacks read/organisation permission; results incomplete for <org>`
|
|
1290
|
+
and reports externally-raised counts as a lower bound rather than guessing from
|
|
1258
1291
|
`authorAssociation` alone.
|
|
1259
1292
|
|
|
1260
1293
|
### Organisation feature gating
|
|
@@ -1604,8 +1637,8 @@ the run, rather than falling back to every repository. The run then collects
|
|
|
1604
1637
|
exactly the repositories that check validated, without listing the
|
|
1605
1638
|
organisation again, and prints `Limited to:` beneath its heading.
|
|
1606
1639
|
|
|
1607
|
-
The remediable categories are the
|
|
1608
|
-
|
|
1640
|
+
The remediable categories are the features with a documented enablement
|
|
1641
|
+
endpoint:
|
|
1609
1642
|
|
|
1610
1643
|
| `--category` | Enables |
|
|
1611
1644
|
|---|---|
|
|
@@ -1615,15 +1648,48 @@ enablement endpoint:
|
|
|
1615
1648
|
| `dependabot_updates_enabled` | Dependabot security updates (plus alerts) |
|
|
1616
1649
|
| `private_vulnerability_reporting` | Private vulnerability reporting |
|
|
1617
1650
|
| `auto_merge` | The repository's "Allow auto-merge" setting |
|
|
1651
|
+
| `code_quality` | GitHub Code Quality, per language (named only; billed) |
|
|
1652
|
+
| `codeql_stale_configurations` | Stale CodeQL cleanup (named only; deletes) |
|
|
1618
1653
|
|
|
1619
1654
|
Qualitative findings (Scorecard, zizmor, open Dependabot alerts, cooldown,
|
|
1620
1655
|
release freshness/mutability) are reported but not auto-remediated. Remediation
|
|
1621
1656
|
is organisation-scoped (`--scope org`, the default and only supported scope).
|
|
1622
1657
|
|
|
1658
|
+
The last two run **only when named** with `--category`, and the no-argument
|
|
1659
|
+
run never includes them: `code_quality`, because GitHub bills it,
|
|
1660
|
+
and `codeql_stale_configurations`, because it deletes data.
|
|
1661
|
+
|
|
1662
|
+
### Enabling GitHub Code Quality
|
|
1663
|
+
|
|
1664
|
+
GitHub Code Quality is billed per active committer and uses GitHub Actions
|
|
1665
|
+
minutes, so switching it on is a spending decision a routine run must not
|
|
1666
|
+
make:
|
|
1667
|
+
|
|
1668
|
+
```bash
|
|
1669
|
+
# Preview: each repository with the languages it would get.
|
|
1670
|
+
uvx github-security-report remediate --org lfreleng-actions \
|
|
1671
|
+
--category code_quality
|
|
1672
|
+
|
|
1673
|
+
# Apply.
|
|
1674
|
+
uvx github-security-report remediate --org lfreleng-actions \
|
|
1675
|
+
--category code_quality --apply
|
|
1676
|
+
```
|
|
1677
|
+
|
|
1678
|
+
It acts on the rows of **Code Quality**, the Disabled and Partial
|
|
1679
|
+
repositories. Each one is configured for the union of the languages its
|
|
1680
|
+
setup already analyses and the ones its Linguist languages make applicable,
|
|
1681
|
+
so a write never drops a configured language; the dry run names them, as in
|
|
1682
|
+
`dependamerge (python, javascript-typescript)`. Not applicable and Unreadable
|
|
1683
|
+
repositories are never written. GitHub provisions the first analysis
|
|
1684
|
+
asynchronously. A `409` (an update already in progress), a `403` (an archived
|
|
1685
|
+
repository, or Code Quality not allowed for it) or a `422` fails that
|
|
1686
|
+
repository only and names the cause. The token needs the classic `repo`
|
|
1687
|
+
scope, or a fine-grained token's Administration: write. See
|
|
1688
|
+
[ADR-0006](docs/adr/0006-github-code-quality.md) for the reasoning.
|
|
1689
|
+
|
|
1623
1690
|
### Cleaning up stale CodeQL configurations
|
|
1624
1691
|
|
|
1625
|
-
|
|
1626
|
-
run never includes it:
|
|
1692
|
+
This category is **destructive**, so it runs only when named:
|
|
1627
1693
|
|
|
1628
1694
|
```bash
|
|
1629
1695
|
# Preview: which configurations would go, and which are refused and why.
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.
|
|
22
|
-
__version_tuple__ = version_tuple = (0,
|
|
21
|
+
__version__ = version = '0.19.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 19, 0)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -32,7 +32,9 @@ contributors. Automation is reported in its own column instead.
|
|
|
32
32
|
|
|
33
33
|
from __future__ import annotations
|
|
34
34
|
|
|
35
|
+
import enum
|
|
35
36
|
from collections.abc import Set
|
|
37
|
+
from dataclasses import dataclass
|
|
36
38
|
|
|
37
39
|
# --------------------------------------------------------------------------- #
|
|
38
40
|
# Automation authors
|
|
@@ -218,3 +220,32 @@ def normalise_members(logins: object) -> frozenset[str]:
|
|
|
218
220
|
for login in logins
|
|
219
221
|
if isinstance(login, str) and (normalised := normalise_login(login))
|
|
220
222
|
)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
class MembershipGap(enum.Enum):
|
|
226
|
+
"""Why organisation membership could not be read in full.
|
|
227
|
+
|
|
228
|
+
Each names a different fix, so the run reports which one applies rather
|
|
229
|
+
than folding them into one "could not be read".
|
|
230
|
+
"""
|
|
231
|
+
|
|
232
|
+
# The token lacks the organisation-membership permission (classic
|
|
233
|
+
# ``read:org``, fine-grained Members: read).
|
|
234
|
+
PERMISSION = "permission"
|
|
235
|
+
# The token's account is outside the organisation, so GitHub served only
|
|
236
|
+
# its public members.
|
|
237
|
+
NOT_MEMBER = "not_member"
|
|
238
|
+
# Anything else: a failed page, a malformed response, the page limit.
|
|
239
|
+
UNREADABLE = "unreadable"
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
@dataclass(frozen=True)
|
|
243
|
+
class OrgMembership:
|
|
244
|
+
"""An organisation membership read: the members, or why there are none.
|
|
245
|
+
|
|
246
|
+
Exactly one of the two is set. ``members`` of ``None`` carries the meaning
|
|
247
|
+
:func:`is_external_author` gives it, and ``gap`` says why.
|
|
248
|
+
"""
|
|
249
|
+
|
|
250
|
+
members: frozenset[str] | None
|
|
251
|
+
gap: MembershipGap | None = None
|
|
@@ -34,6 +34,7 @@ class CategoryKey(str, Enum):
|
|
|
34
34
|
MUTABLE_RELEASES = "mutable_releases"
|
|
35
35
|
PRIVATE_VULNERABILITY_REPORTING = "private_vulnerability_reporting"
|
|
36
36
|
AUTO_MERGE = "auto_merge"
|
|
37
|
+
CODE_QUALITY = "code_quality"
|
|
37
38
|
GITHUB_ISSUES = "github_issues"
|
|
38
39
|
PULL_REQUESTS = "pull_requests"
|
|
39
40
|
PULL_REQUESTS_ASSIGNED = "pull_requests_assigned"
|
|
@@ -66,6 +67,9 @@ class CategoryMeta:
|
|
|
66
67
|
# Alternative pass wording for the collapsed "All <label>" footer line,
|
|
67
68
|
# when the counted wording would not read grammatically after "All".
|
|
68
69
|
pass_all_label: str | None = None
|
|
70
|
+
# Wording for the footer's unknown line, for a category whose unknowns have
|
|
71
|
+
# one specific cause worth naming.
|
|
72
|
+
unknown_label: str = "Unknown"
|
|
69
73
|
# The lowest finding severity that counts as a failure for this category.
|
|
70
74
|
# A repository fails (appears as an offender) only when it carries a finding
|
|
71
75
|
# at or above this rung; findings below it fold into the clean count. The
|
|
@@ -170,6 +170,27 @@ TABLE_CATEGORIES: dict[CategoryKey, CategoryMeta] = {
|
|
|
170
170
|
"vulnerability it fixes stays unpatched."
|
|
171
171
|
),
|
|
172
172
|
),
|
|
173
|
+
CategoryKey.CODE_QUALITY: CategoryMeta(
|
|
174
|
+
key=CategoryKey.CODE_QUALITY,
|
|
175
|
+
title="Code Quality",
|
|
176
|
+
pass_label="Enabled",
|
|
177
|
+
fail_label="Disabled or partial",
|
|
178
|
+
unknown_label="Unreadable",
|
|
179
|
+
url=(
|
|
180
|
+
"https://docs.github.com/en/code-security/concepts/code-quality/"
|
|
181
|
+
"code-quality"
|
|
182
|
+
),
|
|
183
|
+
description=(
|
|
184
|
+
"Repositories holding a language GitHub Code Quality analyses "
|
|
185
|
+
"(python, go, java-kotlin, javascript-typescript, ruby, csharp, as "
|
|
186
|
+
"GitHub's Linguist classifies them) where Code Quality is off "
|
|
187
|
+
"(Disabled) or configured without one of those languages "
|
|
188
|
+
"(Partial). Missing names the languages to add. A repository with "
|
|
189
|
+
"none of them -- shell scripts and workflow files alone, say -- is "
|
|
190
|
+
"Not applicable and never counted as off. Unreadable counts the "
|
|
191
|
+
"repositories whose setup, or whose languages, could not be read."
|
|
192
|
+
),
|
|
193
|
+
),
|
|
173
194
|
CategoryKey.GITHUB_ISSUES: CategoryMeta(
|
|
174
195
|
key=CategoryKey.GITHUB_ISSUES,
|
|
175
196
|
title="GitHub Issues",
|
|
@@ -199,7 +199,7 @@ def report(
|
|
|
199
199
|
)
|
|
200
200
|
hidden = boundary.resolve_hidden(console, hide)
|
|
201
201
|
|
|
202
|
-
cfg = _load_config(config_file, config_data, org, token_env
|
|
202
|
+
cfg, config_path = _load_config(config_file, config_data, org, token_env)
|
|
203
203
|
detected = boundary.detect_target(console, repo, scope)
|
|
204
204
|
mode = boundary.resolve_mode(console, scope, cfg=cfg, detected=detected)
|
|
205
205
|
|
|
@@ -230,6 +230,7 @@ def report(
|
|
|
230
230
|
concurrency=concurrency,
|
|
231
231
|
),
|
|
232
232
|
hidden=hidden,
|
|
233
|
+
config_path=config_path,
|
|
233
234
|
)
|
|
234
235
|
code = boundary.run_guarded(console, _run_org(cfg, options, console=console))
|
|
235
236
|
else:
|
|
@@ -271,6 +272,7 @@ def report(
|
|
|
271
272
|
report_cfg=cfg.report if cfg is not None else None,
|
|
272
273
|
limits=TopNLimits(shared=top_n, report=top_n_report, cli=top_n_cli),
|
|
273
274
|
hidden=hidden,
|
|
275
|
+
config_path=config_path,
|
|
274
276
|
),
|
|
275
277
|
)
|
|
276
278
|
raise typer.Exit(code)
|
|
@@ -301,7 +303,7 @@ def remediate(
|
|
|
301
303
|
category: list[str] | None = typer.Option(
|
|
302
304
|
None,
|
|
303
305
|
"--category",
|
|
304
|
-
help=f"Remediable category to act on (repeatable; default: all except the
|
|
306
|
+
help=f"Remediable category to act on (repeatable; default: all except the explicit-only {_EXPLICIT_HELP}, which delete data or incur charges and run only when named). One of: {_REMEDIABLE_HELP}.",
|
|
305
307
|
),
|
|
306
308
|
token_env: str | None = typer.Option(
|
|
307
309
|
None,
|
|
@@ -325,9 +327,9 @@ def remediate(
|
|
|
325
327
|
Runs the same collection the report uses, then switches on each selected
|
|
326
328
|
remediable feature wherever a repository has it confirmed off. Named
|
|
327
329
|
explicitly, codeql_stale_configurations also deletes orphaned CodeQL
|
|
328
|
-
configurations
|
|
329
|
-
|
|
330
|
-
PAT.
|
|
330
|
+
configurations, and code_quality enables billed GitHub Code Quality. Dry
|
|
331
|
+
run by default: pass --apply to make changes. Requires a write-capable
|
|
332
|
+
token (org admin), distinct from the read-only reporting PAT.
|
|
331
333
|
"""
|
|
332
334
|
console = _console(no_color)
|
|
333
335
|
|
|
@@ -363,7 +365,7 @@ def remediate(
|
|
|
363
365
|
)
|
|
364
366
|
raise typer.Exit(2)
|
|
365
367
|
|
|
366
|
-
cfg = _load_config(config_file, config_data, org, token_env
|
|
368
|
+
cfg, config_path = _load_config(config_file, config_data, org, token_env)
|
|
367
369
|
if cfg is None:
|
|
368
370
|
console.print(
|
|
369
371
|
"[red]No configuration: provide --config, --config-data or --org.[/red]"
|
|
@@ -390,6 +392,7 @@ def remediate(
|
|
|
390
392
|
categories=categories,
|
|
391
393
|
apply=apply,
|
|
392
394
|
repos=selection,
|
|
395
|
+
config_path=config_path,
|
|
393
396
|
),
|
|
394
397
|
)
|
|
395
398
|
raise typer.Exit(code)
|