github-security-report 0.18.0__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.0 → github_security_report-0.19.0}/PKG-INFO +167 -31
- {github_security_report-0.18.0 → github_security_report-0.19.0}/README.md +162 -26
- {github_security_report-0.18.0 → github_security_report-0.19.0}/pyproject.toml +7 -7
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/_version.py +2 -2
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/authors.py +31 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/categories/keys.py +4 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/categories/tables.py +21 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/cli/app.py +20 -6
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/cli/boundary.py +8 -2
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/cli/modes.py +52 -22
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/cli/options.py +12 -7
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/cli/serialise.py +3 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/client/__init__.py +10 -7
- github_security_report-0.19.0/src/github_security_report/client/accounting.py +161 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/client/endpoints.py +8 -0
- github_security_report-0.19.0/src/github_security_report/client/failures.py +115 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/client/org_reads.py +71 -47
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/client/parsers.py +2 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/client/queries.py +12 -3
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/client/reads.py +20 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/client/transport.py +41 -96
- {github_security_report-0.18.0 → 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.0 → github_security_report-0.19.0}/src/github_security_report/collect/extras.py +88 -43
- github_security_report-0.19.0/src/github_security_report/collect/graph.py +106 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/collect/org.py +17 -61
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/collect/protocols.py +11 -4
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/config/__init__.py +2 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/config/loader.py +1 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/config/models.py +7 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/config/schema.py +11 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/models.py +4 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/posture/__init__.py +10 -3
- {github_security_report-0.18.0 → 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.0 → github_security_report-0.19.0}/src/github_security_report/remediate/__init__.py +42 -3
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/remediate/model.py +7 -1
- {github_security_report-0.18.0 → 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.0 → github_security_report-0.19.0}/src/github_security_report/report/aggregate.py +14 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/report/tables.py +13 -2
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/summary.py +22 -6
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/templates/report.html.j2 +2 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/.gitignore +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/LICENSE +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/LICENSES/Apache-2.0.txt +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/scripts/README.md +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/__init__.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/categories/__init__.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/categories/signals.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/classify.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/cli/__init__.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/cli/__main__.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/cli/outputs.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/cli/publish.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/client/alerts.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/client/batch_errors.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/client/codeql_parsers.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/client/copilot.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/client/errors.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/client/reviews.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/codeql/__init__.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/codeql/cleanup.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/codeql/facts.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/codeql/tables.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/collect/__init__.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/collect/codeql.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/collect/context.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/collect/repo.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/collect/scoping.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/config/order.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/gating.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/gitctx.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/issues.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/layout.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/ordering.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/posture/enablement.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/posture/releases.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/pulls/__init__.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/pulls/columns.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/pulls/counting.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/pulls/presentation.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/pulls/table.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/py.typed +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/ranking.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/remediate/codeql.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/render/__init__.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/render/html.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/render/markdown.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/render/slack.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/render/slack_limits.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/report/__init__.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/report/display.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/report/signals.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/rulesets.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/runner.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/scope.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/secret_patterns.py +0 -0
- {github_security_report-0.18.0 → github_security_report-0.19.0}/src/github_security_report/severity.py +0 -0
- {github_security_report-0.18.0 → 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
|
|
@@ -29,16 +29,16 @@ Requires-Dist: jinja2==3.1.6
|
|
|
29
29
|
Requires-Dist: jsonschema==4.26.0
|
|
30
30
|
Requires-Dist: pyyaml==6.0.3
|
|
31
31
|
Requires-Dist: rich==15.0.0
|
|
32
|
-
Requires-Dist: typer==0.27.
|
|
32
|
+
Requires-Dist: typer==0.27.3
|
|
33
33
|
Provides-Extra: dev
|
|
34
|
-
Requires-Dist: mypy==2.
|
|
34
|
+
Requires-Dist: mypy==2.4.0; extra == 'dev'
|
|
35
35
|
Requires-Dist: pytest-asyncio==1.4.0; extra == 'dev'
|
|
36
36
|
Requires-Dist: pytest-cov==7.1.0; extra == 'dev'
|
|
37
37
|
Requires-Dist: pytest==9.1.1; extra == 'dev'
|
|
38
38
|
Requires-Dist: respx==0.23.1; extra == 'dev'
|
|
39
|
-
Requires-Dist: ruff==0.
|
|
39
|
+
Requires-Dist: ruff==0.17.0; extra == 'dev'
|
|
40
40
|
Requires-Dist: syrupy==6.1.1; extra == 'dev'
|
|
41
|
-
Requires-Dist: types-jsonschema==4.26.0.
|
|
41
|
+
Requires-Dist: types-jsonschema==4.26.0.20261006; extra == 'dev'
|
|
42
42
|
Requires-Dist: types-pyyaml==6.0.12.20250915; extra == 'dev'
|
|
43
43
|
Description-Content-Type: text/markdown
|
|
44
44
|
|
|
@@ -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
|
|
@@ -348,6 +376,7 @@ environment-variable name, never embedded.
|
|
|
348
376
|
"issue_age_warn_days": 30,
|
|
349
377
|
"issue_age_error_days": 60,
|
|
350
378
|
"graph_batch": 10,
|
|
379
|
+
"concurrency": 9,
|
|
351
380
|
"order": { "style": "auto" }
|
|
352
381
|
},
|
|
353
382
|
"organizations": [
|
|
@@ -463,10 +492,11 @@ Pages output. The valid category keys are: `codeql`,
|
|
|
463
492
|
`zizmor`, `aislop`, `dependabot_alerts`, `secret_scanning`,
|
|
464
493
|
`dependabot_alerts_enabled`, `dependabot_updates_enabled`,
|
|
465
494
|
`dependabot_cooldown`, `releases`, `mutable_releases`,
|
|
466
|
-
`private_vulnerability_reporting`, `auto_merge`, `
|
|
467
|
-
`pull_requests`, `pull_requests_assigned`. Like the other
|
|
468
|
-
`categories` can be set globally and overridden per
|
|
469
|
-
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
|
|
470
500
|
machine-readable `report.json` artifact always contains the complete dataset,
|
|
471
501
|
regardless of these toggles.
|
|
472
502
|
|
|
@@ -1280,10 +1310,13 @@ see the relationship reports it), but one naming an outsider proves nothing,
|
|
|
1280
1310
|
because that is exactly how a private member appears to an under-privileged
|
|
1281
1311
|
token. Those authors are reported as indeterminate rather than external, so
|
|
1282
1312
|
`Ext` **undercounts and should be read as a lower bound** — the table says so in
|
|
1283
|
-
its description whenever this happens, and the
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
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.
|
|
1287
1320
|
|
|
1288
1321
|
**Automation is never counted as external.** Bots are outsiders by association —
|
|
1289
1322
|
`dependabot[bot]` genuinely reports `CONTRIBUTOR` or `NONE` — so counting on
|
|
@@ -1296,8 +1329,9 @@ association value GitHub has newly introduced — is **not** counted as external
|
|
|
1296
1329
|
The column understates rather than inventing an outsider.
|
|
1297
1330
|
|
|
1298
1331
|
**Permissions.** Reading organisation membership needs `read:org` (classic) or
|
|
1299
|
-
**Members: read** (fine-grained). Without it the
|
|
1300
|
-
|
|
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
|
|
1301
1335
|
`authorAssociation` alone.
|
|
1302
1336
|
|
|
1303
1337
|
### Organisation feature gating
|
|
@@ -1345,16 +1379,25 @@ size is bounded by *latency*, not by rate-limit cost: 25 repositories measured
|
|
|
1345
1379
|
at ~9 s against `lfreleng-actions` and failed intermittently; 10 measured at
|
|
1346
1380
|
~3.5 s.
|
|
1347
1381
|
|
|
1382
|
+
Up to four of these queries run at once. Each takes a few seconds on GitHub's
|
|
1383
|
+
side, so a prefetch issued one query at a time left most of the client's
|
|
1384
|
+
concurrency idle; against `lfreleng-actions` it took about 50 s of a 100 s run.
|
|
1385
|
+
|
|
1348
1386
|
`report.graph_batch` (default `10`; minimum `1`) sets the starting batch size,
|
|
1349
1387
|
globally or per organisation. It is a lever for a slow day rather than a hard
|
|
1350
1388
|
limit: a batch that still fails on size after the transport's own retries is
|
|
1351
|
-
re-issued at **half** the size, and
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1389
|
+
re-issued at **half** the size, and every batch issued after that failure is
|
|
1390
|
+
seen uses the smaller size for the rest of that organisation's collection
|
|
1391
|
+
(each organisation in a multi-org run starts from its own configured size,
|
|
1392
|
+
since organisations differ in how much data a repository carries). The
|
|
1393
|
+
learned size only shrinks. Because queries overlap, the batches already in
|
|
1394
|
+
flight when the first failure arrives were issued at the old size, so on a
|
|
1395
|
+
slow day up to four oversized batches, rather than one, may fail before the
|
|
1396
|
+
smaller size takes effect; that bounded cost buys the overlap. Only a
|
|
1397
|
+
single-repository query GitHub still cannot answer, or a failure that a
|
|
1398
|
+
smaller query could not fix (a `403`, an exhausted rate limit, or a
|
|
1399
|
+
`500`/`503` outage rather than a `502`/`504` timeout), aborts the run with
|
|
1400
|
+
exit code `3`, and the queries still in flight are cancelled first.
|
|
1358
1401
|
|
|
1359
1402
|
The value can be set three ways, in this order of precedence:
|
|
1360
1403
|
|
|
@@ -1379,6 +1422,65 @@ a larger value buys nothing except a longer first failure.
|
|
|
1379
1422
|
> predating the flag will reject a set one with `No such option`, so set
|
|
1380
1423
|
> `GSR_GRAPH_BATCH` only once the pinned release supports it.
|
|
1381
1424
|
|
|
1425
|
+
### Client concurrency
|
|
1426
|
+
|
|
1427
|
+
Each organisation's run holds at most `report.concurrency` requests to the
|
|
1428
|
+
GitHub API open at once (default `9`; minimum `1`, maximum `32`), globally or
|
|
1429
|
+
per organisation. Reads from the external Scorecard API queue on a separate
|
|
1430
|
+
limit of four, so a slow third-party endpoint never holds a slot that a
|
|
1431
|
+
GitHub request is waiting for.
|
|
1432
|
+
|
|
1433
|
+
The value can be set three ways, in this order of precedence:
|
|
1434
|
+
|
|
1435
|
+
1. `--concurrency N` on the command line (the action's `concurrency` input).
|
|
1436
|
+
2. A **repository or organisation variable** passed to that input. The
|
|
1437
|
+
bundled `reporting.yaml` passes `vars.GSR_CONCURRENCY`, so the value can be
|
|
1438
|
+
adjusted from repository settings without a workflow edit or a release.
|
|
1439
|
+
3. `report.concurrency` in the configuration; otherwise the built-in `9`.
|
|
1440
|
+
|
|
1441
|
+
`9` is a deliberate halfway point. GitHub documents its secondary rate limits
|
|
1442
|
+
as no more than 100 concurrent requests across REST and GraphQL, and no more
|
|
1443
|
+
than 900 points per minute for REST endpoints; this project has hit those
|
|
1444
|
+
limits before, so the default stays well clear of them. Measured against
|
|
1445
|
+
`lfreleng-actions` (143 repositories, about 1,400 requests; one run each in
|
|
1446
|
+
the same hour, so read ±10–15 % as GitHub latency noise), before the GraphQL
|
|
1447
|
+
prefetch ran concurrently:
|
|
1448
|
+
|
|
1449
|
+
<!-- markdownlint-disable MD013 -->
|
|
1450
|
+
|
|
1451
|
+
| Concurrency | Wall | Throughput | Mean time per request | Average slots busy | GitHub pushback |
|
|
1452
|
+
| ----------- | ---: | ---------: | --------------------: | -----------------: | --------------- |
|
|
1453
|
+
| 6 (former default) | 102.5 s | 13.7 req/s | 0.283 s | 3.9 / 6 | none |
|
|
1454
|
+
| 12 | 75.9 s | 18.5 req/s | 0.286 s | 5.3 / 12 | none |
|
|
1455
|
+
|
|
1456
|
+
<!-- markdownlint-enable MD013 -->
|
|
1457
|
+
|
|
1458
|
+
Raise it once a run shows no pushback; lower it if GitHub starts answering
|
|
1459
|
+
with secondary rate-limit `403`/`429` responses. To measure a change, run the
|
|
1460
|
+
CLI with `--verbose`: when each organisation's collection finishes it logs
|
|
1461
|
+
one line of what the run cost, for example
|
|
1462
|
+
|
|
1463
|
+
<!-- markdownlint-disable MD013 -->
|
|
1464
|
+
|
|
1465
|
+
```text
|
|
1466
|
+
GitHub API for lfreleng-actions: 1402 requests (0 retries), 822 core points, 64 graphql points, 41.3 MiB received, 102.5 s
|
|
1467
|
+
```
|
|
1468
|
+
|
|
1469
|
+
<!-- markdownlint-enable MD013 -->
|
|
1470
|
+
|
|
1471
|
+
The request count is every request the tool issued, including retries and
|
|
1472
|
+
attempts that never got an answer. The points come from GitHub's own
|
|
1473
|
+
`x-ratelimit-*` response headers rather than from counting: GitHub meters a
|
|
1474
|
+
token across more than one bucket per resource (alike requests seconds apart
|
|
1475
|
+
can carry different `x-ratelimit-reset` values with independent counters), so
|
|
1476
|
+
each bucket's spread between its lowest and highest reading is summed per
|
|
1477
|
+
resource. The lowest reading already includes that request's own cost, so the
|
|
1478
|
+
figure is short by the first request of each bucket — a measurement aid, not a
|
|
1479
|
+
bill. Nothing of this line reaches the rendered report or the step summary.
|
|
1480
|
+
|
|
1481
|
+
> As with `graph_batch`, an empty input is not passed to the tool, so set
|
|
1482
|
+
> `GSR_CONCURRENCY` only once the pinned release supports `--concurrency`.
|
|
1483
|
+
|
|
1382
1484
|
### Pass/fail severity cutoff
|
|
1383
1485
|
|
|
1384
1486
|
The severity-ranked signals (CodeQL, Scorecard, Zizmor, aislop, Dependabot
|
|
@@ -1457,6 +1559,7 @@ and the Slack **bot token** is consumed by the workflow, not the CLI.
|
|
|
1457
1559
|
| `force_notify` | No | `false` | Post to Slack regardless of `report_day` |
|
|
1458
1560
|
| `hide` | No | `""` | Category keys to suppress on every output (space- or comma-separated). Overrides config, and is one-way: it cannot re-enable a disabled category |
|
|
1459
1561
|
| `graph_batch` | No | `""` | Repositories per batched GraphQL prefetch query (minimum `1`; default: config, else `10`). Pass a repository or organisation variable such as `${{ vars.GSR_GRAPH_BATCH }}` to adjust it from settings. A batch that still fails is halved automatically, so this sets the starting size (see [GraphQL prefetch batch size](#graphql-prefetch-batch-size)) |
|
|
1562
|
+
| `concurrency` | No | `""` | Concurrent GitHub API requests per organisation (`1` to `32`; default: config, else `9`). Pass a repository or organisation variable such as `${{ vars.GSR_CONCURRENCY }}` to adjust it from settings (see [Client concurrency](#client-concurrency)) |
|
|
1460
1563
|
| `tool_version` | No | `""` | Published PyPI version to install. Empty (the default) uses the Dependabot-managed pin in `.github/runtime-pin/requirements.txt`; set a specific version to override. Ignored on pull requests or when `use_local_source` is `true` (both run from source) |
|
|
1461
1564
|
| `use_local_source` | No | `false` | Run from the checked-out source instead of PyPI (for testing unreleased code from any event) |
|
|
1462
1565
|
|
|
@@ -1578,8 +1681,8 @@ the run, rather than falling back to every repository. The run then collects
|
|
|
1578
1681
|
exactly the repositories that check validated, without listing the
|
|
1579
1682
|
organisation again, and prints `Limited to:` beneath its heading.
|
|
1580
1683
|
|
|
1581
|
-
The remediable categories are the
|
|
1582
|
-
|
|
1684
|
+
The remediable categories are the features with a documented enablement
|
|
1685
|
+
endpoint:
|
|
1583
1686
|
|
|
1584
1687
|
| `--category` | Enables |
|
|
1585
1688
|
|---|---|
|
|
@@ -1589,15 +1692,48 @@ enablement endpoint:
|
|
|
1589
1692
|
| `dependabot_updates_enabled` | Dependabot security updates (plus alerts) |
|
|
1590
1693
|
| `private_vulnerability_reporting` | Private vulnerability reporting |
|
|
1591
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) |
|
|
1592
1697
|
|
|
1593
1698
|
Qualitative findings (Scorecard, zizmor, open Dependabot alerts, cooldown,
|
|
1594
1699
|
release freshness/mutability) are reported but not auto-remediated. Remediation
|
|
1595
1700
|
is organisation-scoped (`--scope org`, the default and only supported scope).
|
|
1596
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
|
+
|
|
1597
1734
|
### Cleaning up stale CodeQL configurations
|
|
1598
1735
|
|
|
1599
|
-
|
|
1600
|
-
run never includes it:
|
|
1736
|
+
This category is **destructive**, so it runs only when named:
|
|
1601
1737
|
|
|
1602
1738
|
```bash
|
|
1603
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
|
|
@@ -304,6 +332,7 @@ environment-variable name, never embedded.
|
|
|
304
332
|
"issue_age_warn_days": 30,
|
|
305
333
|
"issue_age_error_days": 60,
|
|
306
334
|
"graph_batch": 10,
|
|
335
|
+
"concurrency": 9,
|
|
307
336
|
"order": { "style": "auto" }
|
|
308
337
|
},
|
|
309
338
|
"organizations": [
|
|
@@ -419,10 +448,11 @@ Pages output. The valid category keys are: `codeql`,
|
|
|
419
448
|
`zizmor`, `aislop`, `dependabot_alerts`, `secret_scanning`,
|
|
420
449
|
`dependabot_alerts_enabled`, `dependabot_updates_enabled`,
|
|
421
450
|
`dependabot_cooldown`, `releases`, `mutable_releases`,
|
|
422
|
-
`private_vulnerability_reporting`, `auto_merge`, `
|
|
423
|
-
`pull_requests`, `pull_requests_assigned`. Like the other
|
|
424
|
-
`categories` can be set globally and overridden per
|
|
425
|
-
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
|
|
426
456
|
machine-readable `report.json` artifact always contains the complete dataset,
|
|
427
457
|
regardless of these toggles.
|
|
428
458
|
|
|
@@ -1236,10 +1266,13 @@ see the relationship reports it), but one naming an outsider proves nothing,
|
|
|
1236
1266
|
because that is exactly how a private member appears to an under-privileged
|
|
1237
1267
|
token. Those authors are reported as indeterminate rather than external, so
|
|
1238
1268
|
`Ext` **undercounts and should be read as a lower bound** — the table says so in
|
|
1239
|
-
its description whenever this happens, and the
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
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.
|
|
1243
1276
|
|
|
1244
1277
|
**Automation is never counted as external.** Bots are outsiders by association —
|
|
1245
1278
|
`dependabot[bot]` genuinely reports `CONTRIBUTOR` or `NONE` — so counting on
|
|
@@ -1252,8 +1285,9 @@ association value GitHub has newly introduced — is **not** counted as external
|
|
|
1252
1285
|
The column understates rather than inventing an outsider.
|
|
1253
1286
|
|
|
1254
1287
|
**Permissions.** Reading organisation membership needs `read:org` (classic) or
|
|
1255
|
-
**Members: read** (fine-grained). Without it the
|
|
1256
|
-
|
|
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
|
|
1257
1291
|
`authorAssociation` alone.
|
|
1258
1292
|
|
|
1259
1293
|
### Organisation feature gating
|
|
@@ -1301,16 +1335,25 @@ size is bounded by *latency*, not by rate-limit cost: 25 repositories measured
|
|
|
1301
1335
|
at ~9 s against `lfreleng-actions` and failed intermittently; 10 measured at
|
|
1302
1336
|
~3.5 s.
|
|
1303
1337
|
|
|
1338
|
+
Up to four of these queries run at once. Each takes a few seconds on GitHub's
|
|
1339
|
+
side, so a prefetch issued one query at a time left most of the client's
|
|
1340
|
+
concurrency idle; against `lfreleng-actions` it took about 50 s of a 100 s run.
|
|
1341
|
+
|
|
1304
1342
|
`report.graph_batch` (default `10`; minimum `1`) sets the starting batch size,
|
|
1305
1343
|
globally or per organisation. It is a lever for a slow day rather than a hard
|
|
1306
1344
|
limit: a batch that still fails on size after the transport's own retries is
|
|
1307
|
-
re-issued at **half** the size, and
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1345
|
+
re-issued at **half** the size, and every batch issued after that failure is
|
|
1346
|
+
seen uses the smaller size for the rest of that organisation's collection
|
|
1347
|
+
(each organisation in a multi-org run starts from its own configured size,
|
|
1348
|
+
since organisations differ in how much data a repository carries). The
|
|
1349
|
+
learned size only shrinks. Because queries overlap, the batches already in
|
|
1350
|
+
flight when the first failure arrives were issued at the old size, so on a
|
|
1351
|
+
slow day up to four oversized batches, rather than one, may fail before the
|
|
1352
|
+
smaller size takes effect; that bounded cost buys the overlap. Only a
|
|
1353
|
+
single-repository query GitHub still cannot answer, or a failure that a
|
|
1354
|
+
smaller query could not fix (a `403`, an exhausted rate limit, or a
|
|
1355
|
+
`500`/`503` outage rather than a `502`/`504` timeout), aborts the run with
|
|
1356
|
+
exit code `3`, and the queries still in flight are cancelled first.
|
|
1314
1357
|
|
|
1315
1358
|
The value can be set three ways, in this order of precedence:
|
|
1316
1359
|
|
|
@@ -1335,6 +1378,65 @@ a larger value buys nothing except a longer first failure.
|
|
|
1335
1378
|
> predating the flag will reject a set one with `No such option`, so set
|
|
1336
1379
|
> `GSR_GRAPH_BATCH` only once the pinned release supports it.
|
|
1337
1380
|
|
|
1381
|
+
### Client concurrency
|
|
1382
|
+
|
|
1383
|
+
Each organisation's run holds at most `report.concurrency` requests to the
|
|
1384
|
+
GitHub API open at once (default `9`; minimum `1`, maximum `32`), globally or
|
|
1385
|
+
per organisation. Reads from the external Scorecard API queue on a separate
|
|
1386
|
+
limit of four, so a slow third-party endpoint never holds a slot that a
|
|
1387
|
+
GitHub request is waiting for.
|
|
1388
|
+
|
|
1389
|
+
The value can be set three ways, in this order of precedence:
|
|
1390
|
+
|
|
1391
|
+
1. `--concurrency N` on the command line (the action's `concurrency` input).
|
|
1392
|
+
2. A **repository or organisation variable** passed to that input. The
|
|
1393
|
+
bundled `reporting.yaml` passes `vars.GSR_CONCURRENCY`, so the value can be
|
|
1394
|
+
adjusted from repository settings without a workflow edit or a release.
|
|
1395
|
+
3. `report.concurrency` in the configuration; otherwise the built-in `9`.
|
|
1396
|
+
|
|
1397
|
+
`9` is a deliberate halfway point. GitHub documents its secondary rate limits
|
|
1398
|
+
as no more than 100 concurrent requests across REST and GraphQL, and no more
|
|
1399
|
+
than 900 points per minute for REST endpoints; this project has hit those
|
|
1400
|
+
limits before, so the default stays well clear of them. Measured against
|
|
1401
|
+
`lfreleng-actions` (143 repositories, about 1,400 requests; one run each in
|
|
1402
|
+
the same hour, so read ±10–15 % as GitHub latency noise), before the GraphQL
|
|
1403
|
+
prefetch ran concurrently:
|
|
1404
|
+
|
|
1405
|
+
<!-- markdownlint-disable MD013 -->
|
|
1406
|
+
|
|
1407
|
+
| Concurrency | Wall | Throughput | Mean time per request | Average slots busy | GitHub pushback |
|
|
1408
|
+
| ----------- | ---: | ---------: | --------------------: | -----------------: | --------------- |
|
|
1409
|
+
| 6 (former default) | 102.5 s | 13.7 req/s | 0.283 s | 3.9 / 6 | none |
|
|
1410
|
+
| 12 | 75.9 s | 18.5 req/s | 0.286 s | 5.3 / 12 | none |
|
|
1411
|
+
|
|
1412
|
+
<!-- markdownlint-enable MD013 -->
|
|
1413
|
+
|
|
1414
|
+
Raise it once a run shows no pushback; lower it if GitHub starts answering
|
|
1415
|
+
with secondary rate-limit `403`/`429` responses. To measure a change, run the
|
|
1416
|
+
CLI with `--verbose`: when each organisation's collection finishes it logs
|
|
1417
|
+
one line of what the run cost, for example
|
|
1418
|
+
|
|
1419
|
+
<!-- markdownlint-disable MD013 -->
|
|
1420
|
+
|
|
1421
|
+
```text
|
|
1422
|
+
GitHub API for lfreleng-actions: 1402 requests (0 retries), 822 core points, 64 graphql points, 41.3 MiB received, 102.5 s
|
|
1423
|
+
```
|
|
1424
|
+
|
|
1425
|
+
<!-- markdownlint-enable MD013 -->
|
|
1426
|
+
|
|
1427
|
+
The request count is every request the tool issued, including retries and
|
|
1428
|
+
attempts that never got an answer. The points come from GitHub's own
|
|
1429
|
+
`x-ratelimit-*` response headers rather than from counting: GitHub meters a
|
|
1430
|
+
token across more than one bucket per resource (alike requests seconds apart
|
|
1431
|
+
can carry different `x-ratelimit-reset` values with independent counters), so
|
|
1432
|
+
each bucket's spread between its lowest and highest reading is summed per
|
|
1433
|
+
resource. The lowest reading already includes that request's own cost, so the
|
|
1434
|
+
figure is short by the first request of each bucket — a measurement aid, not a
|
|
1435
|
+
bill. Nothing of this line reaches the rendered report or the step summary.
|
|
1436
|
+
|
|
1437
|
+
> As with `graph_batch`, an empty input is not passed to the tool, so set
|
|
1438
|
+
> `GSR_CONCURRENCY` only once the pinned release supports `--concurrency`.
|
|
1439
|
+
|
|
1338
1440
|
### Pass/fail severity cutoff
|
|
1339
1441
|
|
|
1340
1442
|
The severity-ranked signals (CodeQL, Scorecard, Zizmor, aislop, Dependabot
|
|
@@ -1413,6 +1515,7 @@ and the Slack **bot token** is consumed by the workflow, not the CLI.
|
|
|
1413
1515
|
| `force_notify` | No | `false` | Post to Slack regardless of `report_day` |
|
|
1414
1516
|
| `hide` | No | `""` | Category keys to suppress on every output (space- or comma-separated). Overrides config, and is one-way: it cannot re-enable a disabled category |
|
|
1415
1517
|
| `graph_batch` | No | `""` | Repositories per batched GraphQL prefetch query (minimum `1`; default: config, else `10`). Pass a repository or organisation variable such as `${{ vars.GSR_GRAPH_BATCH }}` to adjust it from settings. A batch that still fails is halved automatically, so this sets the starting size (see [GraphQL prefetch batch size](#graphql-prefetch-batch-size)) |
|
|
1518
|
+
| `concurrency` | No | `""` | Concurrent GitHub API requests per organisation (`1` to `32`; default: config, else `9`). Pass a repository or organisation variable such as `${{ vars.GSR_CONCURRENCY }}` to adjust it from settings (see [Client concurrency](#client-concurrency)) |
|
|
1416
1519
|
| `tool_version` | No | `""` | Published PyPI version to install. Empty (the default) uses the Dependabot-managed pin in `.github/runtime-pin/requirements.txt`; set a specific version to override. Ignored on pull requests or when `use_local_source` is `true` (both run from source) |
|
|
1417
1520
|
| `use_local_source` | No | `false` | Run from the checked-out source instead of PyPI (for testing unreleased code from any event) |
|
|
1418
1521
|
|
|
@@ -1534,8 +1637,8 @@ the run, rather than falling back to every repository. The run then collects
|
|
|
1534
1637
|
exactly the repositories that check validated, without listing the
|
|
1535
1638
|
organisation again, and prints `Limited to:` beneath its heading.
|
|
1536
1639
|
|
|
1537
|
-
The remediable categories are the
|
|
1538
|
-
|
|
1640
|
+
The remediable categories are the features with a documented enablement
|
|
1641
|
+
endpoint:
|
|
1539
1642
|
|
|
1540
1643
|
| `--category` | Enables |
|
|
1541
1644
|
|---|---|
|
|
@@ -1545,15 +1648,48 @@ enablement endpoint:
|
|
|
1545
1648
|
| `dependabot_updates_enabled` | Dependabot security updates (plus alerts) |
|
|
1546
1649
|
| `private_vulnerability_reporting` | Private vulnerability reporting |
|
|
1547
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) |
|
|
1548
1653
|
|
|
1549
1654
|
Qualitative findings (Scorecard, zizmor, open Dependabot alerts, cooldown,
|
|
1550
1655
|
release freshness/mutability) are reported but not auto-remediated. Remediation
|
|
1551
1656
|
is organisation-scoped (`--scope org`, the default and only supported scope).
|
|
1552
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
|
+
|
|
1553
1690
|
### Cleaning up stale CodeQL configurations
|
|
1554
1691
|
|
|
1555
|
-
|
|
1556
|
-
run never includes it:
|
|
1692
|
+
This category is **destructive**, so it runs only when named:
|
|
1557
1693
|
|
|
1558
1694
|
```bash
|
|
1559
1695
|
# Preview: which configurations would go, and which are refused and why.
|