github-security-report 0.15.0__tar.gz → 0.16.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.15.0 → github_security_report-0.16.0}/PKG-INFO +132 -13
- {github_security_report-0.15.0 → github_security_report-0.16.0}/README.md +129 -10
- {github_security_report-0.15.0 → github_security_report-0.16.0}/pyproject.toml +4 -4
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/_version.py +2 -2
- github_security_report-0.16.0/src/github_security_report/categories/__init__.py +92 -0
- github_security_report-0.16.0/src/github_security_report/categories/keys.py +76 -0
- github_security_report-0.16.0/src/github_security_report/categories/signals.py +113 -0
- github_security_report-0.16.0/src/github_security_report/categories/tables.py +244 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/cli/app.py +6 -1
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/cli/modes.py +4 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/cli/outputs.py +55 -12
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/cli/publish.py +4 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/cli/serialise.py +4 -0
- github_security_report-0.16.0/src/github_security_report/client/codeql_parsers.py +119 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/client/org_reads.py +5 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/client/parsers.py +5 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/client/queries.py +12 -3
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/client/reads.py +95 -1
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/client/writes.py +20 -0
- github_security_report-0.16.0/src/github_security_report/codeql/__init__.py +38 -0
- github_security_report-0.16.0/src/github_security_report/codeql/facts.py +189 -0
- github_security_report-0.16.0/src/github_security_report/codeql/tables.py +246 -0
- github_security_report-0.16.0/src/github_security_report/collect/codeql.py +66 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/collect/extras.py +25 -15
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/collect/protocols.py +15 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/config/loader.py +12 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/config/models.py +27 -1
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/config/schema.py +63 -36
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/layout.py +14 -28
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/models.py +6 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/ordering.py +13 -16
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/posture/__init__.py +2 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/posture/enablement.py +29 -4
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/posture/facts.py +2 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/remediate.py +10 -6
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/render/html.py +29 -16
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/render/markdown.py +34 -22
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/render/slack.py +45 -30
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/render/terminal.py +23 -40
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/report/__init__.py +10 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/report/aggregate.py +59 -1
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/report/display.py +47 -1
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/report/tables.py +63 -5
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/summary.py +50 -3
- github_security_report-0.15.0/src/github_security_report/categories.py +0 -384
- {github_security_report-0.15.0 → github_security_report-0.16.0}/.gitignore +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/LICENSE +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/LICENSES/Apache-2.0.txt +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/scripts/README.md +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/__init__.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/authors.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/classify.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/cli/__init__.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/cli/__main__.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/cli/boundary.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/cli/options.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/client/__init__.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/client/alerts.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/client/batch_errors.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/client/copilot.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/client/endpoints.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/client/errors.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/client/reviews.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/client/transport.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/collect/__init__.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/collect/context.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/collect/org.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/collect/repo.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/config/__init__.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/config/order.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/gating.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/gitctx.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/issues.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/posture/releases.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/pulls/__init__.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/pulls/columns.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/pulls/counting.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/pulls/presentation.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/pulls/table.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/py.typed +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/ranking.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/render/__init__.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/render/slack_limits.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/report/signals.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/rulesets.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/runner.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/scope.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/secret_patterns.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/severity.py +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/templates/index.html.j2 +0 -0
- {github_security_report-0.15.0 → github_security_report-0.16.0}/src/github_security_report/templates/report.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.16.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
|
|
@@ -36,8 +36,8 @@ 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.16.
|
|
40
|
-
Requires-Dist: syrupy==6.
|
|
39
|
+
Requires-Dist: ruff==0.16.8; extra == 'dev'
|
|
40
|
+
Requires-Dist: syrupy==6.1.1; extra == 'dev'
|
|
41
41
|
Requires-Dist: types-jsonschema==4.26.0.20260518; extra == 'dev'
|
|
42
42
|
Requires-Dist: types-pyyaml==6.0.12.20250915; extra == 'dev'
|
|
43
43
|
Description-Content-Type: text/markdown
|
|
@@ -101,6 +101,33 @@ if you want to probe everything regardless.
|
|
|
101
101
|
Further sections report **configuration posture** and **freshness** as plain
|
|
102
102
|
tables (org mode):
|
|
103
103
|
|
|
104
|
+
- **CodeQL scan health** — two tables beneath **CodeQL: Results/Findings** (the
|
|
105
|
+
CodeQL alerts table), whose "Clean" says nothing about whether CodeQL is
|
|
106
|
+
still running:
|
|
107
|
+
- **Stale Configurations** lists every CodeQL configuration whose last scan
|
|
108
|
+
trails the default branch's newest commit by more than
|
|
109
|
+
`codeql_stale_days` (default 30) — the condition GitHub's tool status page
|
|
110
|
+
flags as *"Code Scanning results may be out of date"*. Each row names the
|
|
111
|
+
setup type (**Default**, GitHub-managed; or **Advanced**, a workflow in the
|
|
112
|
+
repository), the language, the last *successful* scan, and the cause:
|
|
113
|
+
analyses failing (a run that errors still uploads, but its results do not
|
|
114
|
+
count as a scan), default setup switched off, a workflow removed or
|
|
115
|
+
disabled, or an advanced workflow
|
|
116
|
+
**superseded** by default setup (GitHub rejects advanced CodeQL uploads
|
|
117
|
+
while default setup is on). An *orphaned* configuration will never scan
|
|
118
|
+
again; delete it from the tool status page once the live setup covers its
|
|
119
|
+
language.
|
|
120
|
+
- **Language Coverage** lists repositories where GitHub detects a
|
|
121
|
+
CodeQL-supported language that no current configuration scans — typically
|
|
122
|
+
an advanced workflow whose language matrix omits one the repository
|
|
123
|
+
contains, such as `actions` for its own workflows. On default setup GitHub
|
|
124
|
+
reports only the languages it is set to scan, so a detected language
|
|
125
|
+
deliberately left out of default setup is not visible to this check.
|
|
126
|
+
|
|
127
|
+
Both consider only repositories where CodeQL has run at least once; the rest
|
|
128
|
+
already appear in the Results/Findings not-enabled list. Finding a stale
|
|
129
|
+
configuration means reading each repository's whole CodeQL analysis history,
|
|
130
|
+
since GitHub cannot filter it by configuration.
|
|
104
131
|
- **Dependabot** — three tables: repositories with vulnerability **alerts not
|
|
105
132
|
enabled**, repositories with **security updates not enabled**, and ecosystems
|
|
106
133
|
with no update `cooldown` configured (mandatory; any value passes).
|
|
@@ -118,6 +145,20 @@ tables (org mode):
|
|
|
118
145
|
exposes no org-wide or GraphQL equivalent) and, like every other category,
|
|
119
146
|
always collected; hide it with the `private_vulnerability_reporting` render
|
|
120
147
|
toggle.
|
|
148
|
+
- **Auto-merge** — repositories where the **Allow auto-merge** setting is off,
|
|
149
|
+
so a pull request cannot be queued to merge itself once its requirements are
|
|
150
|
+
met. The setting only offers the option: an auto-merging pull request still
|
|
151
|
+
waits for the required checks, reviews and branch protections the repository
|
|
152
|
+
already enforces, so enabling it relaxes nothing. What it removes is the
|
|
153
|
+
interval between a change becoming mergeable and somebody noticing — the
|
|
154
|
+
window a reviewed dependency update sits in while the vulnerability it fixes
|
|
155
|
+
stays unpatched. Read from the batched GraphQL prefetch, so it costs no extra
|
|
156
|
+
request; hide it with the `auto_merge` render toggle.
|
|
157
|
+
|
|
158
|
+
The four enablement categories count the repositories with and without the
|
|
159
|
+
feature (plus any whose state could not be read), and by default name whichever
|
|
160
|
+
known list is shorter — see
|
|
161
|
+
[Enabled or disabled repository lists](#enabled-or-disabled-repository-lists).
|
|
121
162
|
|
|
122
163
|
## Operating modes
|
|
123
164
|
|
|
@@ -182,6 +223,7 @@ organisation and **Repository access** set to *All repositories*, then grant:
|
|
|
182
223
|
| Secret scanning alerts | Open secret-scanning alerts, across every GitHub pattern category |
|
|
183
224
|
| Issues | Open issues and their labels (GitHub Issues table) |
|
|
184
225
|
| Administration | Dependabot enablement + security-updates status, and effective branch rules |
|
|
226
|
+
| Actions | Workflow state behind a stale CodeQL configuration |
|
|
185
227
|
|
|
186
228
|
**Organization permissions:**
|
|
187
229
|
|
|
@@ -302,6 +344,7 @@ environment-variable name, never embedded.
|
|
|
302
344
|
"include_test": false,
|
|
303
345
|
"repo_min_age_days": 28,
|
|
304
346
|
"release_max_age_days": 60,
|
|
347
|
+
"codeql_stale_days": 30,
|
|
305
348
|
"graph_batch": 10,
|
|
306
349
|
"order": { "style": "auto" }
|
|
307
350
|
},
|
|
@@ -326,6 +369,17 @@ individual output. Set a value to `0` to remove the limit entirely and show
|
|
|
326
369
|
every offender. Each can also be set at the CLI with `--top-n`,
|
|
327
370
|
`--top-n-report`, `--top-n-cli`, and `--top-n-slack`.
|
|
328
371
|
|
|
372
|
+
`report.codeql_stale_days` (default `30`, minimum `1`) is the CodeQL
|
|
373
|
+
stale-configuration threshold: a configuration is stale once its last scan
|
|
374
|
+
trails the default branch's newest commit by more than that many days. It is
|
|
375
|
+
measured against the branch head rather than the clock, so a repository nobody
|
|
376
|
+
has pushed to is not flagged just for being quiet. GitHub does not publish the
|
|
377
|
+
threshold behind its own "may be out of date" warning; across the
|
|
378
|
+
`lfreleng-actions` estate healthy configurations trailed their head by at most
|
|
379
|
+
five days and abandoned ones by more than eighty, so the default sits well
|
|
380
|
+
clear of both. The same threshold decides which configurations count as current
|
|
381
|
+
for Language Coverage.
|
|
382
|
+
|
|
329
383
|
The Releases / Tagging section has two independent freshness levers:
|
|
330
384
|
|
|
331
385
|
- `report.repo_min_age_days` (default `28`, `0` = include all) is a grace
|
|
@@ -355,6 +409,23 @@ The per-org `exclude` list removes repositories from analysis entirely; they are
|
|
|
355
409
|
reported as **excluded** (distinct from "not enabled"), so an intentional
|
|
356
410
|
exclusion is visible rather than silently dropped.
|
|
357
411
|
|
|
412
|
+
Every category reports those exclusions beneath its counts, so an
|
|
413
|
+
organisation-wide list repeats under each one. `report.excluded_display`
|
|
414
|
+
governs that line on every surface:
|
|
415
|
+
|
|
416
|
+
| Value | Excluded line |
|
|
417
|
+
| ----- | ------------- |
|
|
418
|
+
| `always-show` (default) | Shown under every category |
|
|
419
|
+
| `always-hide` | Never shown |
|
|
420
|
+
| `conditional-hide` | Shown only for a category whose exclusions differ from the organisation's `exclude` list, and then in full |
|
|
421
|
+
|
|
422
|
+
Hiding the line changes nothing else: every count is unaffected, and the report
|
|
423
|
+
header's repository count already leaves excluded repositories out. So a clean
|
|
424
|
+
category reads `All Clean` once no Excluded line qualifies it. Today every
|
|
425
|
+
category reports the organisation's own list, so `conditional-hide` currently
|
|
426
|
+
hides the same lines `always-hide` does. It differs once a category excludes
|
|
427
|
+
repositories of its own. The `report.json` artifact always lists the exclusions.
|
|
428
|
+
|
|
358
429
|
Archived and test repositories are excluded from analysis by default. Opt them
|
|
359
430
|
back in with `report.include_archived` / `report.include_test` in the config, or
|
|
360
431
|
for a single run with `--include-archived` / `--include-test`.
|
|
@@ -384,16 +455,17 @@ both true.
|
|
|
384
455
|
|
|
385
456
|
The example above hides Zizmor on every surface, and keeps Releases / Tagging
|
|
386
457
|
out of the terminal and Slack while still publishing it to the Markdown and HTML
|
|
387
|
-
Pages output. The valid category keys are: `codeql`,
|
|
388
|
-
`
|
|
389
|
-
`
|
|
390
|
-
`
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
458
|
+
Pages output. The valid category keys are: `codeql`,
|
|
459
|
+
`codeql_stale_configurations`, `codeql_language_coverage`, `scorecard`,
|
|
460
|
+
`zizmor`, `aislop`, `dependabot_alerts`, `secret_scanning`,
|
|
461
|
+
`dependabot_alerts_enabled`, `dependabot_updates_enabled`,
|
|
462
|
+
`dependabot_cooldown`, `releases`, `mutable_releases`,
|
|
463
|
+
`private_vulnerability_reporting`, `auto_merge`, `github_issues`,
|
|
464
|
+
`pull_requests`, `pull_requests_assigned`. Like the other `report` settings,
|
|
465
|
+
`categories` can be set globally and overridden per organisation (overrides
|
|
466
|
+
merge key-by-key, so flipping one output leaves the rest untouched). The
|
|
467
|
+
machine-readable `report.json` artifact always contains the complete dataset,
|
|
468
|
+
regardless of these toggles.
|
|
397
469
|
|
|
398
470
|
When several organisations share one Slack channel they render into a single
|
|
399
471
|
combined digest, so the per-org Slack toggles are unioned for that channel: a
|
|
@@ -454,6 +526,52 @@ Slack-style ceiling, but they still apply their own row limits — only the
|
|
|
454
526
|
GitHub Pages report whenever `pages_url` is set and short enough to render as a
|
|
455
527
|
link.
|
|
456
528
|
|
|
529
|
+
### Enabled or disabled repository lists
|
|
530
|
+
|
|
531
|
+
The four boolean feature categories — `dependabot_alerts_enabled`,
|
|
532
|
+
`dependabot_updates_enabled`, `private_vulnerability_reporting` and `auto_merge`
|
|
533
|
+
— sort each repository into one of three buckets: enabled, not enabled, or
|
|
534
|
+
**unknown** when the feature's state could not be read. The unknown bucket is
|
|
535
|
+
counted but never named, and never treated as either side, so the enabled and
|
|
536
|
+
not-enabled counts need not sum to the repositories analysed. Beneath the counts,
|
|
537
|
+
one of the two known sides is named; `report.repo_list` chooses which, on every
|
|
538
|
+
surface:
|
|
539
|
+
|
|
540
|
+
| Value | Names |
|
|
541
|
+
| ----- | ----- |
|
|
542
|
+
| `auto` (default) | Whichever list is shorter |
|
|
543
|
+
| `enabled` | The repositories with the feature on |
|
|
544
|
+
| `disabled` | The repositories with the feature off |
|
|
545
|
+
|
|
546
|
+
`auto` keeps a footer short whichever way an organisation leans: a feature
|
|
547
|
+
nearly every repository has lists its few holdouts, and one almost none have
|
|
548
|
+
lists its few adopters. Two cases resolve towards the actionable side: a tie
|
|
549
|
+
names the repositories without the feature, and so does a category where
|
|
550
|
+
*nothing* is enabled, since naming an empty list would print nothing where a
|
|
551
|
+
reader wants the repositories to fix. Both sides are always counted, whichever is
|
|
552
|
+
named.
|
|
553
|
+
|
|
554
|
+
A category can override the global value:
|
|
555
|
+
|
|
556
|
+
```json
|
|
557
|
+
{
|
|
558
|
+
"report": {
|
|
559
|
+
"repo_list": "auto",
|
|
560
|
+
"categories": {
|
|
561
|
+
"dependabot_alerts_enabled": { "repo_list": "disabled" }
|
|
562
|
+
}
|
|
563
|
+
},
|
|
564
|
+
"organizations": [{ "name": "lfreleng-actions" }]
|
|
565
|
+
}
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
Here Dependabot alerts always name the repositories to fix, and the other three
|
|
569
|
+
feature categories name whichever side is shorter. `repo_list` applies only to
|
|
570
|
+
those four categories; setting it on any other is a configuration error, since a
|
|
571
|
+
table with qualitative columns has no enabled list to name. These categories
|
|
572
|
+
render as a name list rather than a one-column table on every surface. The
|
|
573
|
+
`report.json` artifact is unaffected.
|
|
574
|
+
|
|
457
575
|
### Per-category row ordering
|
|
458
576
|
|
|
459
577
|
Each table ships a sensible default ordering — largest backlog first, stalest
|
|
@@ -1413,6 +1531,7 @@ enablement endpoint:
|
|
|
1413
1531
|
| `dependabot_alerts_enabled` | Dependabot vulnerability alerts |
|
|
1414
1532
|
| `dependabot_updates_enabled` | Dependabot security updates (plus alerts) |
|
|
1415
1533
|
| `private_vulnerability_reporting` | Private vulnerability reporting |
|
|
1534
|
+
| `auto_merge` | The repository's "Allow auto-merge" setting |
|
|
1416
1535
|
|
|
1417
1536
|
Qualitative findings (Scorecard, zizmor, open Dependabot alerts, cooldown,
|
|
1418
1537
|
release freshness/mutability) are reported but not auto-remediated. Remediation
|
|
@@ -57,6 +57,33 @@ if you want to probe everything regardless.
|
|
|
57
57
|
Further sections report **configuration posture** and **freshness** as plain
|
|
58
58
|
tables (org mode):
|
|
59
59
|
|
|
60
|
+
- **CodeQL scan health** — two tables beneath **CodeQL: Results/Findings** (the
|
|
61
|
+
CodeQL alerts table), whose "Clean" says nothing about whether CodeQL is
|
|
62
|
+
still running:
|
|
63
|
+
- **Stale Configurations** lists every CodeQL configuration whose last scan
|
|
64
|
+
trails the default branch's newest commit by more than
|
|
65
|
+
`codeql_stale_days` (default 30) — the condition GitHub's tool status page
|
|
66
|
+
flags as *"Code Scanning results may be out of date"*. Each row names the
|
|
67
|
+
setup type (**Default**, GitHub-managed; or **Advanced**, a workflow in the
|
|
68
|
+
repository), the language, the last *successful* scan, and the cause:
|
|
69
|
+
analyses failing (a run that errors still uploads, but its results do not
|
|
70
|
+
count as a scan), default setup switched off, a workflow removed or
|
|
71
|
+
disabled, or an advanced workflow
|
|
72
|
+
**superseded** by default setup (GitHub rejects advanced CodeQL uploads
|
|
73
|
+
while default setup is on). An *orphaned* configuration will never scan
|
|
74
|
+
again; delete it from the tool status page once the live setup covers its
|
|
75
|
+
language.
|
|
76
|
+
- **Language Coverage** lists repositories where GitHub detects a
|
|
77
|
+
CodeQL-supported language that no current configuration scans — typically
|
|
78
|
+
an advanced workflow whose language matrix omits one the repository
|
|
79
|
+
contains, such as `actions` for its own workflows. On default setup GitHub
|
|
80
|
+
reports only the languages it is set to scan, so a detected language
|
|
81
|
+
deliberately left out of default setup is not visible to this check.
|
|
82
|
+
|
|
83
|
+
Both consider only repositories where CodeQL has run at least once; the rest
|
|
84
|
+
already appear in the Results/Findings not-enabled list. Finding a stale
|
|
85
|
+
configuration means reading each repository's whole CodeQL analysis history,
|
|
86
|
+
since GitHub cannot filter it by configuration.
|
|
60
87
|
- **Dependabot** — three tables: repositories with vulnerability **alerts not
|
|
61
88
|
enabled**, repositories with **security updates not enabled**, and ecosystems
|
|
62
89
|
with no update `cooldown` configured (mandatory; any value passes).
|
|
@@ -74,6 +101,20 @@ tables (org mode):
|
|
|
74
101
|
exposes no org-wide or GraphQL equivalent) and, like every other category,
|
|
75
102
|
always collected; hide it with the `private_vulnerability_reporting` render
|
|
76
103
|
toggle.
|
|
104
|
+
- **Auto-merge** — repositories where the **Allow auto-merge** setting is off,
|
|
105
|
+
so a pull request cannot be queued to merge itself once its requirements are
|
|
106
|
+
met. The setting only offers the option: an auto-merging pull request still
|
|
107
|
+
waits for the required checks, reviews and branch protections the repository
|
|
108
|
+
already enforces, so enabling it relaxes nothing. What it removes is the
|
|
109
|
+
interval between a change becoming mergeable and somebody noticing — the
|
|
110
|
+
window a reviewed dependency update sits in while the vulnerability it fixes
|
|
111
|
+
stays unpatched. Read from the batched GraphQL prefetch, so it costs no extra
|
|
112
|
+
request; hide it with the `auto_merge` render toggle.
|
|
113
|
+
|
|
114
|
+
The four enablement categories count the repositories with and without the
|
|
115
|
+
feature (plus any whose state could not be read), and by default name whichever
|
|
116
|
+
known list is shorter — see
|
|
117
|
+
[Enabled or disabled repository lists](#enabled-or-disabled-repository-lists).
|
|
77
118
|
|
|
78
119
|
## Operating modes
|
|
79
120
|
|
|
@@ -138,6 +179,7 @@ organisation and **Repository access** set to *All repositories*, then grant:
|
|
|
138
179
|
| Secret scanning alerts | Open secret-scanning alerts, across every GitHub pattern category |
|
|
139
180
|
| Issues | Open issues and their labels (GitHub Issues table) |
|
|
140
181
|
| Administration | Dependabot enablement + security-updates status, and effective branch rules |
|
|
182
|
+
| Actions | Workflow state behind a stale CodeQL configuration |
|
|
141
183
|
|
|
142
184
|
**Organization permissions:**
|
|
143
185
|
|
|
@@ -258,6 +300,7 @@ environment-variable name, never embedded.
|
|
|
258
300
|
"include_test": false,
|
|
259
301
|
"repo_min_age_days": 28,
|
|
260
302
|
"release_max_age_days": 60,
|
|
303
|
+
"codeql_stale_days": 30,
|
|
261
304
|
"graph_batch": 10,
|
|
262
305
|
"order": { "style": "auto" }
|
|
263
306
|
},
|
|
@@ -282,6 +325,17 @@ individual output. Set a value to `0` to remove the limit entirely and show
|
|
|
282
325
|
every offender. Each can also be set at the CLI with `--top-n`,
|
|
283
326
|
`--top-n-report`, `--top-n-cli`, and `--top-n-slack`.
|
|
284
327
|
|
|
328
|
+
`report.codeql_stale_days` (default `30`, minimum `1`) is the CodeQL
|
|
329
|
+
stale-configuration threshold: a configuration is stale once its last scan
|
|
330
|
+
trails the default branch's newest commit by more than that many days. It is
|
|
331
|
+
measured against the branch head rather than the clock, so a repository nobody
|
|
332
|
+
has pushed to is not flagged just for being quiet. GitHub does not publish the
|
|
333
|
+
threshold behind its own "may be out of date" warning; across the
|
|
334
|
+
`lfreleng-actions` estate healthy configurations trailed their head by at most
|
|
335
|
+
five days and abandoned ones by more than eighty, so the default sits well
|
|
336
|
+
clear of both. The same threshold decides which configurations count as current
|
|
337
|
+
for Language Coverage.
|
|
338
|
+
|
|
285
339
|
The Releases / Tagging section has two independent freshness levers:
|
|
286
340
|
|
|
287
341
|
- `report.repo_min_age_days` (default `28`, `0` = include all) is a grace
|
|
@@ -311,6 +365,23 @@ The per-org `exclude` list removes repositories from analysis entirely; they are
|
|
|
311
365
|
reported as **excluded** (distinct from "not enabled"), so an intentional
|
|
312
366
|
exclusion is visible rather than silently dropped.
|
|
313
367
|
|
|
368
|
+
Every category reports those exclusions beneath its counts, so an
|
|
369
|
+
organisation-wide list repeats under each one. `report.excluded_display`
|
|
370
|
+
governs that line on every surface:
|
|
371
|
+
|
|
372
|
+
| Value | Excluded line |
|
|
373
|
+
| ----- | ------------- |
|
|
374
|
+
| `always-show` (default) | Shown under every category |
|
|
375
|
+
| `always-hide` | Never shown |
|
|
376
|
+
| `conditional-hide` | Shown only for a category whose exclusions differ from the organisation's `exclude` list, and then in full |
|
|
377
|
+
|
|
378
|
+
Hiding the line changes nothing else: every count is unaffected, and the report
|
|
379
|
+
header's repository count already leaves excluded repositories out. So a clean
|
|
380
|
+
category reads `All Clean` once no Excluded line qualifies it. Today every
|
|
381
|
+
category reports the organisation's own list, so `conditional-hide` currently
|
|
382
|
+
hides the same lines `always-hide` does. It differs once a category excludes
|
|
383
|
+
repositories of its own. The `report.json` artifact always lists the exclusions.
|
|
384
|
+
|
|
314
385
|
Archived and test repositories are excluded from analysis by default. Opt them
|
|
315
386
|
back in with `report.include_archived` / `report.include_test` in the config, or
|
|
316
387
|
for a single run with `--include-archived` / `--include-test`.
|
|
@@ -340,16 +411,17 @@ both true.
|
|
|
340
411
|
|
|
341
412
|
The example above hides Zizmor on every surface, and keeps Releases / Tagging
|
|
342
413
|
out of the terminal and Slack while still publishing it to the Markdown and HTML
|
|
343
|
-
Pages output. The valid category keys are: `codeql`,
|
|
344
|
-
`
|
|
345
|
-
`
|
|
346
|
-
`
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
414
|
+
Pages output. The valid category keys are: `codeql`,
|
|
415
|
+
`codeql_stale_configurations`, `codeql_language_coverage`, `scorecard`,
|
|
416
|
+
`zizmor`, `aislop`, `dependabot_alerts`, `secret_scanning`,
|
|
417
|
+
`dependabot_alerts_enabled`, `dependabot_updates_enabled`,
|
|
418
|
+
`dependabot_cooldown`, `releases`, `mutable_releases`,
|
|
419
|
+
`private_vulnerability_reporting`, `auto_merge`, `github_issues`,
|
|
420
|
+
`pull_requests`, `pull_requests_assigned`. Like the other `report` settings,
|
|
421
|
+
`categories` can be set globally and overridden per organisation (overrides
|
|
422
|
+
merge key-by-key, so flipping one output leaves the rest untouched). The
|
|
423
|
+
machine-readable `report.json` artifact always contains the complete dataset,
|
|
424
|
+
regardless of these toggles.
|
|
353
425
|
|
|
354
426
|
When several organisations share one Slack channel they render into a single
|
|
355
427
|
combined digest, so the per-org Slack toggles are unioned for that channel: a
|
|
@@ -410,6 +482,52 @@ Slack-style ceiling, but they still apply their own row limits — only the
|
|
|
410
482
|
GitHub Pages report whenever `pages_url` is set and short enough to render as a
|
|
411
483
|
link.
|
|
412
484
|
|
|
485
|
+
### Enabled or disabled repository lists
|
|
486
|
+
|
|
487
|
+
The four boolean feature categories — `dependabot_alerts_enabled`,
|
|
488
|
+
`dependabot_updates_enabled`, `private_vulnerability_reporting` and `auto_merge`
|
|
489
|
+
— sort each repository into one of three buckets: enabled, not enabled, or
|
|
490
|
+
**unknown** when the feature's state could not be read. The unknown bucket is
|
|
491
|
+
counted but never named, and never treated as either side, so the enabled and
|
|
492
|
+
not-enabled counts need not sum to the repositories analysed. Beneath the counts,
|
|
493
|
+
one of the two known sides is named; `report.repo_list` chooses which, on every
|
|
494
|
+
surface:
|
|
495
|
+
|
|
496
|
+
| Value | Names |
|
|
497
|
+
| ----- | ----- |
|
|
498
|
+
| `auto` (default) | Whichever list is shorter |
|
|
499
|
+
| `enabled` | The repositories with the feature on |
|
|
500
|
+
| `disabled` | The repositories with the feature off |
|
|
501
|
+
|
|
502
|
+
`auto` keeps a footer short whichever way an organisation leans: a feature
|
|
503
|
+
nearly every repository has lists its few holdouts, and one almost none have
|
|
504
|
+
lists its few adopters. Two cases resolve towards the actionable side: a tie
|
|
505
|
+
names the repositories without the feature, and so does a category where
|
|
506
|
+
*nothing* is enabled, since naming an empty list would print nothing where a
|
|
507
|
+
reader wants the repositories to fix. Both sides are always counted, whichever is
|
|
508
|
+
named.
|
|
509
|
+
|
|
510
|
+
A category can override the global value:
|
|
511
|
+
|
|
512
|
+
```json
|
|
513
|
+
{
|
|
514
|
+
"report": {
|
|
515
|
+
"repo_list": "auto",
|
|
516
|
+
"categories": {
|
|
517
|
+
"dependabot_alerts_enabled": { "repo_list": "disabled" }
|
|
518
|
+
}
|
|
519
|
+
},
|
|
520
|
+
"organizations": [{ "name": "lfreleng-actions" }]
|
|
521
|
+
}
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
Here Dependabot alerts always name the repositories to fix, and the other three
|
|
525
|
+
feature categories name whichever side is shorter. `repo_list` applies only to
|
|
526
|
+
those four categories; setting it on any other is a configuration error, since a
|
|
527
|
+
table with qualitative columns has no enabled list to name. These categories
|
|
528
|
+
render as a name list rather than a one-column table on every surface. The
|
|
529
|
+
`report.json` artifact is unaffected.
|
|
530
|
+
|
|
413
531
|
### Per-category row ordering
|
|
414
532
|
|
|
415
533
|
Each table ships a sensible default ordering — largest backlog first, stalest
|
|
@@ -1369,6 +1487,7 @@ enablement endpoint:
|
|
|
1369
1487
|
| `dependabot_alerts_enabled` | Dependabot vulnerability alerts |
|
|
1370
1488
|
| `dependabot_updates_enabled` | Dependabot security updates (plus alerts) |
|
|
1371
1489
|
| `private_vulnerability_reporting` | Private vulnerability reporting |
|
|
1490
|
+
| `auto_merge` | The repository's "Allow auto-merge" setting |
|
|
1372
1491
|
|
|
1373
1492
|
Qualitative findings (Scorecard, zizmor, open Dependabot alerts, cooldown,
|
|
1374
1493
|
release freshness/mutability) are reported but not auto-remediated. Remediation
|
|
@@ -63,9 +63,9 @@ dev = [
|
|
|
63
63
|
"pytest-asyncio==1.4.0",
|
|
64
64
|
"pytest-cov==7.1.0",
|
|
65
65
|
"respx==0.23.1",
|
|
66
|
-
"syrupy==6.
|
|
66
|
+
"syrupy==6.1.1",
|
|
67
67
|
"mypy==2.3.1",
|
|
68
|
-
"ruff==0.16.
|
|
68
|
+
"ruff==0.16.8",
|
|
69
69
|
"types-jsonschema==4.26.0.20260518",
|
|
70
70
|
"types-PyYAML==6.0.12.20250915",
|
|
71
71
|
]
|
|
@@ -95,9 +95,9 @@ dev = [
|
|
|
95
95
|
"pytest-asyncio==1.4.0",
|
|
96
96
|
"pytest-cov==7.1.0",
|
|
97
97
|
"respx==0.23.1",
|
|
98
|
-
"syrupy==6.
|
|
98
|
+
"syrupy==6.1.1",
|
|
99
99
|
"mypy==2.3.1",
|
|
100
|
-
"ruff==0.16.
|
|
100
|
+
"ruff==0.16.8",
|
|
101
101
|
"types-jsonschema==4.26.0.20260518",
|
|
102
102
|
"types-PyYAML==6.0.12.20250915",
|
|
103
103
|
]
|
|
@@ -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.16.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 16, 0)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# SPDX-FileCopyrightText: 2026 The Linux Foundation
|
|
3
|
+
"""Report category metadata.
|
|
4
|
+
|
|
5
|
+
A single, render-surface-agnostic registry describing every reporting category
|
|
6
|
+
the tool produces. Each category carries its display title, the pass/fail
|
|
7
|
+
vocabulary used in the standardised summary footer, a documentation URL, and a
|
|
8
|
+
default human description. Renderers read this registry instead of hard-coding
|
|
9
|
+
per-category headings, labels and explanatory text, so a wording change here
|
|
10
|
+
flows to the terminal, Slack, Markdown and HTML surfaces at once.
|
|
11
|
+
|
|
12
|
+
The registry deliberately holds no behaviour and imports nothing from the rest
|
|
13
|
+
of the package except the leaf ``severity`` and ``secret_patterns`` modules
|
|
14
|
+
(which themselves import nothing from the package), so both the domain models
|
|
15
|
+
and the renderers can depend on it without a cycle. It is split by kind --
|
|
16
|
+
the ranked signals in :mod:`.signals`, the tables in :mod:`.tables` -- and
|
|
17
|
+
merged here, so callers see one registry in render order.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from github_security_report.categories.keys import CategoryKey, CategoryMeta
|
|
23
|
+
from github_security_report.categories.signals import SIGNAL_CATEGORIES
|
|
24
|
+
from github_security_report.categories.tables import TABLE_CATEGORIES
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"NESTED_CATEGORIES",
|
|
28
|
+
"REPO_LIST_CATEGORIES",
|
|
29
|
+
"CategoryKey",
|
|
30
|
+
"CategoryMeta",
|
|
31
|
+
"all_categories",
|
|
32
|
+
"category_meta",
|
|
33
|
+
"orderable_categories",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
_CATEGORIES: dict[CategoryKey, CategoryMeta] = {**SIGNAL_CATEGORIES, **TABLE_CATEGORIES}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def category_meta(key: CategoryKey) -> CategoryMeta:
|
|
40
|
+
"""The :class:`CategoryMeta` for ``key`` (registry lookup)."""
|
|
41
|
+
return _CATEGORIES[key]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# Categories rendered as sub-tables beneath another category rather than as
|
|
45
|
+
# sections of their own. The three Dependabot posture tables qualify their
|
|
46
|
+
# parent signal -- "Alerts Enabled" means nothing adrift from "Dependabot:
|
|
47
|
+
# Security Alerts" -- so they travel with it and cannot be positioned
|
|
48
|
+
# independently, and the two CodeQL scan-health tables likewise qualify the
|
|
49
|
+
# CodeQL signal, whose "Clean" means nothing if the scans behind it stopped.
|
|
50
|
+
# Named here rather than in the layout module so the config schema can refuse to
|
|
51
|
+
# accept one in an ordering list, which would otherwise be a setting that
|
|
52
|
+
# validates and then does nothing.
|
|
53
|
+
NESTED_CATEGORIES: frozenset[CategoryKey] = frozenset(
|
|
54
|
+
{
|
|
55
|
+
CategoryKey.CODEQL_STALE_CONFIGURATIONS,
|
|
56
|
+
CategoryKey.CODEQL_LANGUAGE_COVERAGE,
|
|
57
|
+
CategoryKey.DEPENDABOT_ALERTS_ENABLED,
|
|
58
|
+
CategoryKey.DEPENDABOT_UPDATES_ENABLED,
|
|
59
|
+
CategoryKey.DEPENDABOT_COOLDOWN,
|
|
60
|
+
}
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# The boolean feature categories: every repository is either enabled or not, and
|
|
64
|
+
# nothing else is known about it, so both sides are plain repository lists and
|
|
65
|
+
# either can be the one worth naming. These render their names inline rather
|
|
66
|
+
# than as a one-column table, and are the only categories the ``repo_list``
|
|
67
|
+
# setting applies to -- the schema refuses it anywhere else, since a table with
|
|
68
|
+
# qualitative columns has no "enabled" list to swap in.
|
|
69
|
+
REPO_LIST_CATEGORIES: frozenset[CategoryKey] = frozenset(
|
|
70
|
+
{
|
|
71
|
+
CategoryKey.DEPENDABOT_ALERTS_ENABLED,
|
|
72
|
+
CategoryKey.DEPENDABOT_UPDATES_ENABLED,
|
|
73
|
+
CategoryKey.PRIVATE_VULNERABILITY_REPORTING,
|
|
74
|
+
CategoryKey.AUTO_MERGE,
|
|
75
|
+
}
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def orderable_categories() -> tuple[CategoryMeta, ...]:
|
|
80
|
+
"""Categories an ordering list may name, in registry order.
|
|
81
|
+
|
|
82
|
+
Every category except the nested ones, which have no position of their own
|
|
83
|
+
to configure.
|
|
84
|
+
"""
|
|
85
|
+
return tuple(
|
|
86
|
+
meta for meta in _CATEGORIES.values() if meta.key not in NESTED_CATEGORIES
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def all_categories() -> tuple[CategoryMeta, ...]:
|
|
91
|
+
"""Every category's metadata, in registry (render) order."""
|
|
92
|
+
return tuple(_CATEGORIES.values())
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# SPDX-FileCopyrightText: 2026 The Linux Foundation
|
|
3
|
+
"""The category identifier and the shape of its metadata.
|
|
4
|
+
|
|
5
|
+
Leaf types shared by the registry halves in :mod:`.signals` and :mod:`.tables`.
|
|
6
|
+
``CategoryKey`` values are the stable identifiers used by the per-category
|
|
7
|
+
configuration toggles, so treat them as part of the config contract: rename
|
|
8
|
+
with care.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from enum import Enum
|
|
15
|
+
|
|
16
|
+
from github_security_report.severity import Severity
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CategoryKey(str, Enum):
|
|
20
|
+
"""Stable identifier for one reporting category (also the config key)."""
|
|
21
|
+
|
|
22
|
+
CODEQL = "codeql"
|
|
23
|
+
CODEQL_STALE_CONFIGURATIONS = "codeql_stale_configurations"
|
|
24
|
+
CODEQL_LANGUAGE_COVERAGE = "codeql_language_coverage"
|
|
25
|
+
SCORECARD = "scorecard"
|
|
26
|
+
ZIZMOR = "zizmor"
|
|
27
|
+
AISLOP = "aislop"
|
|
28
|
+
DEPENDABOT_ALERTS = "dependabot_alerts"
|
|
29
|
+
SECRET_SCANNING = "secret_scanning"
|
|
30
|
+
DEPENDABOT_ALERTS_ENABLED = "dependabot_alerts_enabled"
|
|
31
|
+
DEPENDABOT_UPDATES_ENABLED = "dependabot_updates_enabled"
|
|
32
|
+
DEPENDABOT_COOLDOWN = "dependabot_cooldown"
|
|
33
|
+
RELEASES = "releases"
|
|
34
|
+
MUTABLE_RELEASES = "mutable_releases"
|
|
35
|
+
PRIVATE_VULNERABILITY_REPORTING = "private_vulnerability_reporting"
|
|
36
|
+
AUTO_MERGE = "auto_merge"
|
|
37
|
+
GITHUB_ISSUES = "github_issues"
|
|
38
|
+
PULL_REQUESTS = "pull_requests"
|
|
39
|
+
PULL_REQUESTS_ASSIGNED = "pull_requests_assigned"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass(frozen=True)
|
|
43
|
+
class CategoryMeta:
|
|
44
|
+
"""Display and documentation metadata for one reporting category.
|
|
45
|
+
|
|
46
|
+
``pass_label`` names the healthy state (e.g. ``"Clean"``, ``"Immutable"``)
|
|
47
|
+
and is what the summary footer reports as ``All <pass_label>`` when nothing
|
|
48
|
+
needs attention. That collapse wants an adjectival label; a category whose
|
|
49
|
+
counted wording is a noun phrase ("12 No open issues") sets
|
|
50
|
+
``pass_all_label`` to the word that reads correctly after "All" instead.
|
|
51
|
+
``fail_label`` names the actionable state for categories
|
|
52
|
+
with a binary pass/fail axis (enablement, cooldown, mutability, release
|
|
53
|
+
freshness); it is ``None`` for the severity-ranked signals, whose offenders
|
|
54
|
+
are enumerated in the table itself rather than as a single failure count.
|
|
55
|
+
``description`` is the default explanatory text shown beneath the table on
|
|
56
|
+
the Markdown and HTML surfaces; a builder may override it at runtime when
|
|
57
|
+
the wording depends on configuration (e.g. the release-age thresholds).
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
key: CategoryKey
|
|
61
|
+
title: str
|
|
62
|
+
pass_label: str
|
|
63
|
+
fail_label: str | None
|
|
64
|
+
url: str
|
|
65
|
+
description: str = ""
|
|
66
|
+
# Alternative pass wording for the collapsed "All <label>" footer line,
|
|
67
|
+
# when the counted wording would not read grammatically after "All".
|
|
68
|
+
pass_all_label: str | None = None
|
|
69
|
+
# The lowest finding severity that counts as a failure for this category.
|
|
70
|
+
# A repository fails (appears as an offender) only when it carries a finding
|
|
71
|
+
# at or above this rung; findings below it fold into the clean count. The
|
|
72
|
+
# global default is MEDIUM, so Low and Informational findings pass; a
|
|
73
|
+
# category may lower it (Zizmor uses INFORMATIONAL, so every finding
|
|
74
|
+
# counts). Meaningful only for the severity-ranked signals; binary
|
|
75
|
+
# categories ignore it. Overridable per category via the JSON config.
|
|
76
|
+
fail_severity: Severity = Severity.MEDIUM
|