github-security-report 0.2.0__tar.gz → 0.4.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.2.0 → github_security_report-0.4.0}/PKG-INFO +89 -13
- {github_security_report-0.2.0 → github_security_report-0.4.0}/README.md +86 -10
- {github_security_report-0.2.0 → github_security_report-0.4.0}/pyproject.toml +4 -4
- {github_security_report-0.2.0 → github_security_report-0.4.0}/src/github_security_report/_version.py +2 -2
- github_security_report-0.4.0/src/github_security_report/categories.py +223 -0
- {github_security_report-0.2.0 → github_security_report-0.4.0}/src/github_security_report/classify.py +70 -16
- {github_security_report-0.2.0 → github_security_report-0.4.0}/src/github_security_report/cli.py +99 -30
- {github_security_report-0.2.0 → github_security_report-0.4.0}/src/github_security_report/client.py +196 -12
- {github_security_report-0.2.0 → github_security_report-0.4.0}/src/github_security_report/collect.py +13 -1
- {github_security_report-0.2.0 → github_security_report-0.4.0}/src/github_security_report/config.py +180 -2
- {github_security_report-0.2.0 → github_security_report-0.4.0}/src/github_security_report/models.py +62 -15
- {github_security_report-0.2.0 → github_security_report-0.4.0}/src/github_security_report/posture.py +35 -88
- github_security_report-0.4.0/src/github_security_report/render/html.py +262 -0
- github_security_report-0.4.0/src/github_security_report/render/markdown.py +291 -0
- {github_security_report-0.2.0 → github_security_report-0.4.0}/src/github_security_report/render/slack.py +119 -81
- github_security_report-0.4.0/src/github_security_report/render/terminal.py +223 -0
- github_security_report-0.4.0/src/github_security_report/report.py +355 -0
- {github_security_report-0.2.0 → github_security_report-0.4.0}/src/github_security_report/severity.py +25 -13
- github_security_report-0.4.0/src/github_security_report/templates/report.html.j2 +148 -0
- github_security_report-0.2.0/src/github_security_report/render/html.py +0 -158
- github_security_report-0.2.0/src/github_security_report/render/markdown.py +0 -209
- github_security_report-0.2.0/src/github_security_report/render/terminal.py +0 -166
- github_security_report-0.2.0/src/github_security_report/report.py +0 -214
- github_security_report-0.2.0/src/github_security_report/templates/report.html.j2 +0 -195
- {github_security_report-0.2.0 → github_security_report-0.4.0}/.gitignore +0 -0
- {github_security_report-0.2.0 → github_security_report-0.4.0}/LICENSE +0 -0
- {github_security_report-0.2.0 → github_security_report-0.4.0}/LICENSES/Apache-2.0.txt +0 -0
- {github_security_report-0.2.0 → github_security_report-0.4.0}/scripts/README.md +0 -0
- {github_security_report-0.2.0 → github_security_report-0.4.0}/src/github_security_report/__init__.py +0 -0
- {github_security_report-0.2.0 → github_security_report-0.4.0}/src/github_security_report/gitctx.py +0 -0
- {github_security_report-0.2.0 → github_security_report-0.4.0}/src/github_security_report/py.typed +0 -0
- {github_security_report-0.2.0 → github_security_report-0.4.0}/src/github_security_report/render/__init__.py +0 -0
- {github_security_report-0.2.0 → github_security_report-0.4.0}/src/github_security_report/rulesets.py +0 -0
- {github_security_report-0.2.0 → github_security_report-0.4.0}/src/github_security_report/runner.py +0 -0
- {github_security_report-0.2.0 → github_security_report-0.4.0}/src/github_security_report/scope.py +0 -0
- {github_security_report-0.2.0 → github_security_report-0.4.0}/src/github_security_report/templates/index.html.j2 +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: github-security-report
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.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
|
|
@@ -34,9 +34,9 @@ Provides-Extra: dev
|
|
|
34
34
|
Requires-Dist: mypy==2.1.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
|
-
Requires-Dist: pytest==9.1.
|
|
37
|
+
Requires-Dist: pytest==9.1.1; extra == 'dev'
|
|
38
38
|
Requires-Dist: respx==0.23.1; extra == 'dev'
|
|
39
|
-
Requires-Dist: ruff==0.15.
|
|
39
|
+
Requires-Dist: ruff==0.15.18; extra == 'dev'
|
|
40
40
|
Requires-Dist: syrupy==5.3.2; 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'
|
|
@@ -70,9 +70,16 @@ states and rendered worst-first:
|
|
|
70
70
|
|
|
71
71
|
- **Offenders** — enabled with open findings (a ranked table row).
|
|
72
72
|
- **Clean** — enabled with zero findings (a count beneath the table).
|
|
73
|
-
- **Not enabled** — supported but switched off (a
|
|
74
|
-
|
|
75
|
-
- **Unknown** — indeterminate (insufficient permission),
|
|
73
|
+
- **Not enabled** — supported but switched off (a counted "disabled" footer
|
|
74
|
+
line, with the affected repositories named).
|
|
75
|
+
- **Unknown** — indeterminate (insufficient permission), counted separately.
|
|
76
|
+
|
|
77
|
+
Every category renders the same **standardised summary footer** beneath its
|
|
78
|
+
table: remediation-first count lines (failures, disabled, unknown, then the
|
|
79
|
+
healthy pass line, then excluded). The pass line reads **"All <state>"** when
|
|
80
|
+
nothing needs attention, or **"N <state>"** otherwise. The terminal and Slack
|
|
81
|
+
stay brevity-first; the explanatory per-category description and documentation
|
|
82
|
+
link are shown only on the richer Markdown and HTML (GitHub Pages) outputs.
|
|
76
83
|
|
|
77
84
|
The single GitHub code-scanning feed is partitioned by `tool.name` into CodeQL,
|
|
78
85
|
Scorecard, and zizmor; Scorecard prefers the external aggregate score and falls
|
|
@@ -90,9 +97,10 @@ tables (org mode):
|
|
|
90
97
|
release/tag staleness (repository age never affects ordering; a repository
|
|
91
98
|
with no release or tag ranks highest). Repositories younger than
|
|
92
99
|
`repo_min_age_days` (default 28; `0` includes all) and those in
|
|
93
|
-
`releases_exclude` are omitted.
|
|
94
|
-
|
|
95
|
-
|
|
100
|
+
`releases_exclude` are omitted. A repository is flagged only when its newest
|
|
101
|
+
release or tag is older than `release_max_age_days` (default 60; `0` flags
|
|
102
|
+
every eligible repository), so a repository released or tagged within that
|
|
103
|
+
window counts as recently maintained and drops out of the table.
|
|
96
104
|
|
|
97
105
|
## Operating modes
|
|
98
106
|
|
|
@@ -222,7 +230,7 @@ environment-variable name, never embedded.
|
|
|
222
230
|
"include_archived": false,
|
|
223
231
|
"include_test": false,
|
|
224
232
|
"repo_min_age_days": 28,
|
|
225
|
-
"release_max_age_days":
|
|
233
|
+
"release_max_age_days": 60
|
|
226
234
|
},
|
|
227
235
|
"organizations": [
|
|
228
236
|
{
|
|
@@ -251,10 +259,10 @@ The Releases / Tagging section has two independent freshness levers:
|
|
|
251
259
|
period that omits **brand-new repositories** — those *created* within that
|
|
252
260
|
many days — before a release or tag is expected of them. CLI:
|
|
253
261
|
`--repo-min-age-days`.
|
|
254
|
-
- `report.release_max_age_days` (default `0` = flag everything) is the
|
|
262
|
+
- `report.release_max_age_days` (default `60`; `0` = flag everything) is the
|
|
255
263
|
release-staleness threshold: a repository is only flagged when its newest
|
|
256
264
|
release **or** tag is older than that many days (a repository with neither is
|
|
257
|
-
always flagged).
|
|
265
|
+
always flagged). Tune it to match your release cadence so actively released
|
|
258
266
|
repositories drop out of the table. CLI: `--release-max-age-days`.
|
|
259
267
|
|
|
260
268
|
The per-org `releases_exclude` (CLI `--releases-exclude`, repeatable) drops
|
|
@@ -269,6 +277,74 @@ The per-org `exclude` list removes repositories from analysis entirely; they are
|
|
|
269
277
|
reported as **excluded** (distinct from "not enabled"), so an intentional
|
|
270
278
|
exclusion is visible rather than silently dropped.
|
|
271
279
|
|
|
280
|
+
### Per-category render toggles
|
|
281
|
+
|
|
282
|
+
Every reporting category can be switched on or off, globally and per output
|
|
283
|
+
surface, under `report.categories`. Data is **always** collected; these toggles
|
|
284
|
+
govern presentation only. Each category key takes an `enabled` switch (highest
|
|
285
|
+
precedence — `false` hides it everywhere) and a lower-precedence `outputs` map
|
|
286
|
+
for the four surfaces (`cli`, `slack`, `markdown`, `html`). Everything defaults
|
|
287
|
+
to `true`, so an omitted category or key stays fully enabled. A category is
|
|
288
|
+
rendered on a surface only when `enabled` **and** that surface's toggle are
|
|
289
|
+
both true.
|
|
290
|
+
|
|
291
|
+
```json
|
|
292
|
+
{
|
|
293
|
+
"report": {
|
|
294
|
+
"categories": {
|
|
295
|
+
"zizmor": { "enabled": false },
|
|
296
|
+
"releases": { "outputs": { "cli": false, "slack": false } }
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
"organizations": [{ "name": "lfreleng-actions" }]
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
The example above hides Zizmor on every surface, and keeps Releases / Tagging
|
|
304
|
+
out of the terminal and Slack while still publishing it to the Markdown and HTML
|
|
305
|
+
Pages output. The valid category keys are: `codeql`, `scorecard`, `zizmor`,
|
|
306
|
+
`dependabot_alerts`, `secret_scanning`, `dependabot_alerts_enabled`,
|
|
307
|
+
`dependabot_updates_enabled`, `dependabot_cooldown`, `releases`,
|
|
308
|
+
`mutable_releases`. Like the other `report` settings, `categories` can be set
|
|
309
|
+
globally and overridden per organisation (overrides merge key-by-key, so
|
|
310
|
+
flipping one output leaves the rest untouched). The machine-readable
|
|
311
|
+
`report.json` artifact always contains the complete dataset, regardless of these
|
|
312
|
+
toggles.
|
|
313
|
+
|
|
314
|
+
When several organisations share one Slack channel they render into a single
|
|
315
|
+
combined digest, so the per-org Slack toggles are unioned for that channel: a
|
|
316
|
+
category appears if **any** contributing org would show it on Slack. An org-level
|
|
317
|
+
Slack disable therefore does not suppress a category in a shared-channel digest
|
|
318
|
+
unless every org sharing that channel also disables it (this mirrors the
|
|
319
|
+
most-generous `top_n` rule applied to the same grouping). The terminal, Markdown
|
|
320
|
+
and HTML surfaces are per-org and are not affected by this union.
|
|
321
|
+
|
|
322
|
+
### Pass/fail severity cutoff
|
|
323
|
+
|
|
324
|
+
The severity-ranked signals (CodeQL, Scorecard, Zizmor, Dependabot alerts) use a
|
|
325
|
+
`fail_severity` cutoff to decide when a repository counts as a failure. A
|
|
326
|
+
repository is flagged as an offender only when it carries a finding **at or
|
|
327
|
+
above** the cutoff; findings below it fold into the clean count. Severities run
|
|
328
|
+
(lowest to highest) `informational`, `low`, `medium`, `high`, `critical` —
|
|
329
|
+
`informational` being the new sub-low rung that SARIF `note`/`none` findings
|
|
330
|
+
(the bulk of a tool like Zizmor) normalise to.
|
|
331
|
+
|
|
332
|
+
The global default cutoff is `medium`, so `low` and `informational` findings
|
|
333
|
+
pass. Zizmor defaults to `low` (only `informational` passes). Override the
|
|
334
|
+
cutoff per category under `report.categories.<key>.fail_severity`:
|
|
335
|
+
|
|
336
|
+
```json
|
|
337
|
+
{
|
|
338
|
+
"report": {
|
|
339
|
+
"categories": {
|
|
340
|
+
"codeql": { "fail_severity": "low" },
|
|
341
|
+
"zizmor": { "fail_severity": "informational" }
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
"organizations": [{ "name": "lfreleng-actions" }]
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
272
348
|
`slack.channel` is optional. The action's `slack_channel` input (wired to the
|
|
273
349
|
`SLACK_CHANNEL_ID` variable in `reporting.yaml`) overrides it, so the channel
|
|
274
350
|
can live as an org/repo variable rather than in the config JSON. It must be the
|
|
@@ -314,7 +390,7 @@ and the Slack **bot token** is consumed by the workflow, not the CLI.
|
|
|
314
390
|
| `top_n_slack` | No | — | Offenders per signal in the Slack digest (`0` = no limit; overrides `top_n`) |
|
|
315
391
|
| `fail_threshold` | No | `none` | `none`/`low`/`medium`/`high`/`critical`/`any` (repo mode) |
|
|
316
392
|
| `force_notify` | No | `false` | Post to Slack regardless of `report_day` |
|
|
317
|
-
| `tool_version` | No | `
|
|
393
|
+
| `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) |
|
|
318
394
|
| `use_local_source` | No | `false` | Run from the checked-out source instead of PyPI (for testing unreleased code from any event) |
|
|
319
395
|
|
|
320
396
|
<!-- markdownlint-enable MD013 -->
|
|
@@ -26,9 +26,16 @@ states and rendered worst-first:
|
|
|
26
26
|
|
|
27
27
|
- **Offenders** — enabled with open findings (a ranked table row).
|
|
28
28
|
- **Clean** — enabled with zero findings (a count beneath the table).
|
|
29
|
-
- **Not enabled** — supported but switched off (a
|
|
30
|
-
|
|
31
|
-
- **Unknown** — indeterminate (insufficient permission),
|
|
29
|
+
- **Not enabled** — supported but switched off (a counted "disabled" footer
|
|
30
|
+
line, with the affected repositories named).
|
|
31
|
+
- **Unknown** — indeterminate (insufficient permission), counted separately.
|
|
32
|
+
|
|
33
|
+
Every category renders the same **standardised summary footer** beneath its
|
|
34
|
+
table: remediation-first count lines (failures, disabled, unknown, then the
|
|
35
|
+
healthy pass line, then excluded). The pass line reads **"All <state>"** when
|
|
36
|
+
nothing needs attention, or **"N <state>"** otherwise. The terminal and Slack
|
|
37
|
+
stay brevity-first; the explanatory per-category description and documentation
|
|
38
|
+
link are shown only on the richer Markdown and HTML (GitHub Pages) outputs.
|
|
32
39
|
|
|
33
40
|
The single GitHub code-scanning feed is partitioned by `tool.name` into CodeQL,
|
|
34
41
|
Scorecard, and zizmor; Scorecard prefers the external aggregate score and falls
|
|
@@ -46,9 +53,10 @@ tables (org mode):
|
|
|
46
53
|
release/tag staleness (repository age never affects ordering; a repository
|
|
47
54
|
with no release or tag ranks highest). Repositories younger than
|
|
48
55
|
`repo_min_age_days` (default 28; `0` includes all) and those in
|
|
49
|
-
`releases_exclude` are omitted.
|
|
50
|
-
|
|
51
|
-
|
|
56
|
+
`releases_exclude` are omitted. A repository is flagged only when its newest
|
|
57
|
+
release or tag is older than `release_max_age_days` (default 60; `0` flags
|
|
58
|
+
every eligible repository), so a repository released or tagged within that
|
|
59
|
+
window counts as recently maintained and drops out of the table.
|
|
52
60
|
|
|
53
61
|
## Operating modes
|
|
54
62
|
|
|
@@ -178,7 +186,7 @@ environment-variable name, never embedded.
|
|
|
178
186
|
"include_archived": false,
|
|
179
187
|
"include_test": false,
|
|
180
188
|
"repo_min_age_days": 28,
|
|
181
|
-
"release_max_age_days":
|
|
189
|
+
"release_max_age_days": 60
|
|
182
190
|
},
|
|
183
191
|
"organizations": [
|
|
184
192
|
{
|
|
@@ -207,10 +215,10 @@ The Releases / Tagging section has two independent freshness levers:
|
|
|
207
215
|
period that omits **brand-new repositories** — those *created* within that
|
|
208
216
|
many days — before a release or tag is expected of them. CLI:
|
|
209
217
|
`--repo-min-age-days`.
|
|
210
|
-
- `report.release_max_age_days` (default `0` = flag everything) is the
|
|
218
|
+
- `report.release_max_age_days` (default `60`; `0` = flag everything) is the
|
|
211
219
|
release-staleness threshold: a repository is only flagged when its newest
|
|
212
220
|
release **or** tag is older than that many days (a repository with neither is
|
|
213
|
-
always flagged).
|
|
221
|
+
always flagged). Tune it to match your release cadence so actively released
|
|
214
222
|
repositories drop out of the table. CLI: `--release-max-age-days`.
|
|
215
223
|
|
|
216
224
|
The per-org `releases_exclude` (CLI `--releases-exclude`, repeatable) drops
|
|
@@ -225,6 +233,74 @@ The per-org `exclude` list removes repositories from analysis entirely; they are
|
|
|
225
233
|
reported as **excluded** (distinct from "not enabled"), so an intentional
|
|
226
234
|
exclusion is visible rather than silently dropped.
|
|
227
235
|
|
|
236
|
+
### Per-category render toggles
|
|
237
|
+
|
|
238
|
+
Every reporting category can be switched on or off, globally and per output
|
|
239
|
+
surface, under `report.categories`. Data is **always** collected; these toggles
|
|
240
|
+
govern presentation only. Each category key takes an `enabled` switch (highest
|
|
241
|
+
precedence — `false` hides it everywhere) and a lower-precedence `outputs` map
|
|
242
|
+
for the four surfaces (`cli`, `slack`, `markdown`, `html`). Everything defaults
|
|
243
|
+
to `true`, so an omitted category or key stays fully enabled. A category is
|
|
244
|
+
rendered on a surface only when `enabled` **and** that surface's toggle are
|
|
245
|
+
both true.
|
|
246
|
+
|
|
247
|
+
```json
|
|
248
|
+
{
|
|
249
|
+
"report": {
|
|
250
|
+
"categories": {
|
|
251
|
+
"zizmor": { "enabled": false },
|
|
252
|
+
"releases": { "outputs": { "cli": false, "slack": false } }
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
"organizations": [{ "name": "lfreleng-actions" }]
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
The example above hides Zizmor on every surface, and keeps Releases / Tagging
|
|
260
|
+
out of the terminal and Slack while still publishing it to the Markdown and HTML
|
|
261
|
+
Pages output. The valid category keys are: `codeql`, `scorecard`, `zizmor`,
|
|
262
|
+
`dependabot_alerts`, `secret_scanning`, `dependabot_alerts_enabled`,
|
|
263
|
+
`dependabot_updates_enabled`, `dependabot_cooldown`, `releases`,
|
|
264
|
+
`mutable_releases`. Like the other `report` settings, `categories` can be set
|
|
265
|
+
globally and overridden per organisation (overrides merge key-by-key, so
|
|
266
|
+
flipping one output leaves the rest untouched). The machine-readable
|
|
267
|
+
`report.json` artifact always contains the complete dataset, regardless of these
|
|
268
|
+
toggles.
|
|
269
|
+
|
|
270
|
+
When several organisations share one Slack channel they render into a single
|
|
271
|
+
combined digest, so the per-org Slack toggles are unioned for that channel: a
|
|
272
|
+
category appears if **any** contributing org would show it on Slack. An org-level
|
|
273
|
+
Slack disable therefore does not suppress a category in a shared-channel digest
|
|
274
|
+
unless every org sharing that channel also disables it (this mirrors the
|
|
275
|
+
most-generous `top_n` rule applied to the same grouping). The terminal, Markdown
|
|
276
|
+
and HTML surfaces are per-org and are not affected by this union.
|
|
277
|
+
|
|
278
|
+
### Pass/fail severity cutoff
|
|
279
|
+
|
|
280
|
+
The severity-ranked signals (CodeQL, Scorecard, Zizmor, Dependabot alerts) use a
|
|
281
|
+
`fail_severity` cutoff to decide when a repository counts as a failure. A
|
|
282
|
+
repository is flagged as an offender only when it carries a finding **at or
|
|
283
|
+
above** the cutoff; findings below it fold into the clean count. Severities run
|
|
284
|
+
(lowest to highest) `informational`, `low`, `medium`, `high`, `critical` —
|
|
285
|
+
`informational` being the new sub-low rung that SARIF `note`/`none` findings
|
|
286
|
+
(the bulk of a tool like Zizmor) normalise to.
|
|
287
|
+
|
|
288
|
+
The global default cutoff is `medium`, so `low` and `informational` findings
|
|
289
|
+
pass. Zizmor defaults to `low` (only `informational` passes). Override the
|
|
290
|
+
cutoff per category under `report.categories.<key>.fail_severity`:
|
|
291
|
+
|
|
292
|
+
```json
|
|
293
|
+
{
|
|
294
|
+
"report": {
|
|
295
|
+
"categories": {
|
|
296
|
+
"codeql": { "fail_severity": "low" },
|
|
297
|
+
"zizmor": { "fail_severity": "informational" }
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
"organizations": [{ "name": "lfreleng-actions" }]
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
228
304
|
`slack.channel` is optional. The action's `slack_channel` input (wired to the
|
|
229
305
|
`SLACK_CHANNEL_ID` variable in `reporting.yaml`) overrides it, so the channel
|
|
230
306
|
can live as an org/repo variable rather than in the config JSON. It must be the
|
|
@@ -270,7 +346,7 @@ and the Slack **bot token** is consumed by the workflow, not the CLI.
|
|
|
270
346
|
| `top_n_slack` | No | — | Offenders per signal in the Slack digest (`0` = no limit; overrides `top_n`) |
|
|
271
347
|
| `fail_threshold` | No | `none` | `none`/`low`/`medium`/`high`/`critical`/`any` (repo mode) |
|
|
272
348
|
| `force_notify` | No | `false` | Post to Slack regardless of `report_day` |
|
|
273
|
-
| `tool_version` | No | `
|
|
349
|
+
| `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) |
|
|
274
350
|
| `use_local_source` | No | `false` | Run from the checked-out source instead of PyPI (for testing unreleased code from any event) |
|
|
275
351
|
|
|
276
352
|
<!-- markdownlint-enable MD013 -->
|
|
@@ -59,13 +59,13 @@ Issues = "https://github.com/lfreleng-actions/github-security-report-action/issu
|
|
|
59
59
|
|
|
60
60
|
[project.optional-dependencies]
|
|
61
61
|
dev = [
|
|
62
|
-
"pytest==9.1.
|
|
62
|
+
"pytest==9.1.1",
|
|
63
63
|
"pytest-asyncio==1.4.0",
|
|
64
64
|
"pytest-cov==7.1.0",
|
|
65
65
|
"respx==0.23.1",
|
|
66
66
|
"syrupy==5.3.2",
|
|
67
67
|
"mypy==2.1.0",
|
|
68
|
-
"ruff==0.15.
|
|
68
|
+
"ruff==0.15.18",
|
|
69
69
|
"types-jsonschema==4.26.0.20260518",
|
|
70
70
|
"types-PyYAML==6.0.12.20250915",
|
|
71
71
|
]
|
|
@@ -91,13 +91,13 @@ include = ["src", "README.md", "LICENSE*", "pyproject.toml"]
|
|
|
91
91
|
|
|
92
92
|
[dependency-groups]
|
|
93
93
|
dev = [
|
|
94
|
-
"pytest==9.1.
|
|
94
|
+
"pytest==9.1.1",
|
|
95
95
|
"pytest-asyncio==1.4.0",
|
|
96
96
|
"pytest-cov==7.1.0",
|
|
97
97
|
"respx==0.23.1",
|
|
98
98
|
"syrupy==5.3.2",
|
|
99
99
|
"mypy==2.1.0",
|
|
100
|
-
"ruff==0.15.
|
|
100
|
+
"ruff==0.15.18",
|
|
101
101
|
"types-jsonschema==4.26.0.20260518",
|
|
102
102
|
"types-PyYAML==6.0.12.20250915",
|
|
103
103
|
]
|
{github_security_report-0.2.0 → github_security_report-0.4.0}/src/github_security_report/_version.py
RENAMED
|
@@ -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.4.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 4, 0)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,223 @@
|
|
|
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`` module (which itself imports nothing
|
|
14
|
+
from the package), so both the domain models and the renderers can depend on it
|
|
15
|
+
without a cycle. ``key`` values are the stable identifiers used by the
|
|
16
|
+
per-category configuration toggles, so treat them as part of the config
|
|
17
|
+
contract: rename with care.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from dataclasses import dataclass
|
|
23
|
+
from enum import Enum
|
|
24
|
+
|
|
25
|
+
from github_security_report.severity import Severity
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class CategoryKey(str, Enum):
|
|
29
|
+
"""Stable identifier for one reporting category (also the config key)."""
|
|
30
|
+
|
|
31
|
+
CODEQL = "codeql"
|
|
32
|
+
SCORECARD = "scorecard"
|
|
33
|
+
ZIZMOR = "zizmor"
|
|
34
|
+
DEPENDABOT_ALERTS = "dependabot_alerts"
|
|
35
|
+
SECRET_SCANNING = "secret_scanning"
|
|
36
|
+
DEPENDABOT_ALERTS_ENABLED = "dependabot_alerts_enabled"
|
|
37
|
+
DEPENDABOT_UPDATES_ENABLED = "dependabot_updates_enabled"
|
|
38
|
+
DEPENDABOT_COOLDOWN = "dependabot_cooldown"
|
|
39
|
+
RELEASES = "releases"
|
|
40
|
+
MUTABLE_RELEASES = "mutable_releases"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass(frozen=True)
|
|
44
|
+
class CategoryMeta:
|
|
45
|
+
"""Display and documentation metadata for one reporting category.
|
|
46
|
+
|
|
47
|
+
``pass_label`` names the healthy state (e.g. ``"Clean"``, ``"Immutable"``)
|
|
48
|
+
and is what the summary footer reports as ``All <pass_label>`` when nothing
|
|
49
|
+
needs attention. ``fail_label`` names the actionable state for categories
|
|
50
|
+
with a binary pass/fail axis (enablement, cooldown, mutability, release
|
|
51
|
+
freshness); it is ``None`` for the severity-ranked signals, whose offenders
|
|
52
|
+
are enumerated in the table itself rather than as a single failure count.
|
|
53
|
+
``description`` is the default explanatory text shown beneath the table on
|
|
54
|
+
the Markdown and HTML surfaces; a builder may override it at runtime when
|
|
55
|
+
the wording depends on configuration (e.g. the release-age thresholds).
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
key: CategoryKey
|
|
59
|
+
title: str
|
|
60
|
+
pass_label: str
|
|
61
|
+
fail_label: str | None
|
|
62
|
+
url: str
|
|
63
|
+
description: str = ""
|
|
64
|
+
# The lowest finding severity that counts as a failure for this category.
|
|
65
|
+
# A repository fails (appears as an offender) only when it carries a finding
|
|
66
|
+
# at or above this rung; findings below it fold into the clean count. The
|
|
67
|
+
# global default is MEDIUM, so Low and Informational findings pass; a
|
|
68
|
+
# category may lower it (Zizmor uses LOW, so only Informational passes).
|
|
69
|
+
# Meaningful only for the severity-ranked signals; binary categories ignore
|
|
70
|
+
# it. Overridable per category via the JSON config.
|
|
71
|
+
fail_severity: Severity = Severity.MEDIUM
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
_CATEGORIES: dict[CategoryKey, CategoryMeta] = {
|
|
75
|
+
CategoryKey.CODEQL: CategoryMeta(
|
|
76
|
+
key=CategoryKey.CODEQL,
|
|
77
|
+
title="CodeQL",
|
|
78
|
+
pass_label="Clean",
|
|
79
|
+
fail_label=None,
|
|
80
|
+
url="https://codeql.github.com/",
|
|
81
|
+
description=(
|
|
82
|
+
"CodeQL code-scanning findings, ranked worst-first by severity. "
|
|
83
|
+
"Each row shows a repository's open-alert counts."
|
|
84
|
+
),
|
|
85
|
+
),
|
|
86
|
+
CategoryKey.SCORECARD: CategoryMeta(
|
|
87
|
+
key=CategoryKey.SCORECARD,
|
|
88
|
+
title="OpenSSF Scorecard",
|
|
89
|
+
pass_label="Clean",
|
|
90
|
+
fail_label=None,
|
|
91
|
+
url="https://github.com/ossf/scorecard",
|
|
92
|
+
description=(
|
|
93
|
+
"OpenSSF Scorecard supply-chain health scores (a lower score is "
|
|
94
|
+
"weaker), ranked weakest-first."
|
|
95
|
+
),
|
|
96
|
+
),
|
|
97
|
+
CategoryKey.ZIZMOR: CategoryMeta(
|
|
98
|
+
key=CategoryKey.ZIZMOR,
|
|
99
|
+
title="Zizmor Static Analysis",
|
|
100
|
+
pass_label="Clean",
|
|
101
|
+
fail_label=None,
|
|
102
|
+
url="https://github.com/zizmorcore/zizmor",
|
|
103
|
+
description=(
|
|
104
|
+
"Zizmor static analysis of GitHub Actions workflows, ranked "
|
|
105
|
+
"worst-first by severity."
|
|
106
|
+
),
|
|
107
|
+
# Zizmor's advisory "note" findings normalise to informational; only
|
|
108
|
+
# those pass. A low-or-higher zizmor finding is treated as a failure.
|
|
109
|
+
fail_severity=Severity.LOW,
|
|
110
|
+
),
|
|
111
|
+
CategoryKey.DEPENDABOT_ALERTS: CategoryMeta(
|
|
112
|
+
key=CategoryKey.DEPENDABOT_ALERTS,
|
|
113
|
+
title="Dependabot: Security Alerts",
|
|
114
|
+
pass_label="Clean",
|
|
115
|
+
fail_label=None,
|
|
116
|
+
url=(
|
|
117
|
+
"https://docs.github.com/en/code-security/dependabot/"
|
|
118
|
+
"dependabot-alerts/about-dependabot-alerts"
|
|
119
|
+
),
|
|
120
|
+
description=(
|
|
121
|
+
"Open Dependabot alerts for vulnerable dependencies, counted by "
|
|
122
|
+
"severity per repository."
|
|
123
|
+
),
|
|
124
|
+
),
|
|
125
|
+
CategoryKey.SECRET_SCANNING: CategoryMeta(
|
|
126
|
+
key=CategoryKey.SECRET_SCANNING,
|
|
127
|
+
title="Secret scanning",
|
|
128
|
+
pass_label="Clean",
|
|
129
|
+
fail_label=None,
|
|
130
|
+
url=(
|
|
131
|
+
"https://docs.github.com/en/code-security/secret-scanning/"
|
|
132
|
+
"about-secret-scanning"
|
|
133
|
+
),
|
|
134
|
+
description=(
|
|
135
|
+
"Open secret-scanning alerts. Each row shows a repository's count "
|
|
136
|
+
"of detected, unresolved secrets."
|
|
137
|
+
),
|
|
138
|
+
),
|
|
139
|
+
CategoryKey.DEPENDABOT_ALERTS_ENABLED: CategoryMeta(
|
|
140
|
+
key=CategoryKey.DEPENDABOT_ALERTS_ENABLED,
|
|
141
|
+
title="Dependabot: Alerts Enabled",
|
|
142
|
+
pass_label="Enabled",
|
|
143
|
+
fail_label="Not enabled",
|
|
144
|
+
url=(
|
|
145
|
+
"https://docs.github.com/en/code-security/dependabot/"
|
|
146
|
+
"dependabot-alerts/configuring-dependabot-alerts"
|
|
147
|
+
),
|
|
148
|
+
description=(
|
|
149
|
+
"Repositories with Dependabot security alerts disabled. Enable "
|
|
150
|
+
"them so vulnerable dependencies surface as alerts."
|
|
151
|
+
),
|
|
152
|
+
),
|
|
153
|
+
CategoryKey.DEPENDABOT_UPDATES_ENABLED: CategoryMeta(
|
|
154
|
+
key=CategoryKey.DEPENDABOT_UPDATES_ENABLED,
|
|
155
|
+
title="Dependabot: Security Updates",
|
|
156
|
+
pass_label="Enabled",
|
|
157
|
+
fail_label="Not enabled",
|
|
158
|
+
url=(
|
|
159
|
+
"https://docs.github.com/en/code-security/concepts/"
|
|
160
|
+
"supply-chain-security/dependabot-security-updates"
|
|
161
|
+
),
|
|
162
|
+
description=(
|
|
163
|
+
"Repositories with Dependabot security updates disabled. Enable "
|
|
164
|
+
"them so fixes for vulnerable dependencies arrive as pull requests "
|
|
165
|
+
"automatically."
|
|
166
|
+
),
|
|
167
|
+
),
|
|
168
|
+
CategoryKey.DEPENDABOT_COOLDOWN: CategoryMeta(
|
|
169
|
+
key=CategoryKey.DEPENDABOT_COOLDOWN,
|
|
170
|
+
title="Dependabot: Cooldown Settings",
|
|
171
|
+
pass_label="Enabled",
|
|
172
|
+
fail_label="Without cooldown",
|
|
173
|
+
url=(
|
|
174
|
+
"https://docs.github.com/en/code-security/reference/"
|
|
175
|
+
"supply-chain-security/dependabot-options-reference#cooldown-"
|
|
176
|
+
),
|
|
177
|
+
description=(
|
|
178
|
+
"Repositories whose Dependabot configuration omits an update "
|
|
179
|
+
"cooldown. A cooldown is mandatory; any cooldown value passes. "
|
|
180
|
+
"Repositories with no Dependabot configuration do not appear here."
|
|
181
|
+
),
|
|
182
|
+
),
|
|
183
|
+
CategoryKey.RELEASES: CategoryMeta(
|
|
184
|
+
key=CategoryKey.RELEASES,
|
|
185
|
+
title="Releases / Tagging",
|
|
186
|
+
pass_label="Current",
|
|
187
|
+
fail_label="Overdue",
|
|
188
|
+
url=(
|
|
189
|
+
"https://docs.github.com/en/repositories/"
|
|
190
|
+
"releasing-projects-on-github/about-releases"
|
|
191
|
+
),
|
|
192
|
+
description=(
|
|
193
|
+
"Repositories ranked by combined release and tag staleness "
|
|
194
|
+
"(oldest first). A repository with neither a release nor a tag "
|
|
195
|
+
"ranks highest."
|
|
196
|
+
),
|
|
197
|
+
),
|
|
198
|
+
CategoryKey.MUTABLE_RELEASES: CategoryMeta(
|
|
199
|
+
key=CategoryKey.MUTABLE_RELEASES,
|
|
200
|
+
title="Mutable Releases",
|
|
201
|
+
pass_label="Immutable",
|
|
202
|
+
fail_label="Mutable",
|
|
203
|
+
url=(
|
|
204
|
+
"https://docs.github.com/en/code-security/concepts/"
|
|
205
|
+
"supply-chain-security/immutable-releases"
|
|
206
|
+
),
|
|
207
|
+
description=(
|
|
208
|
+
"Repositories whose latest or last-published release is mutable. "
|
|
209
|
+
"Republish them as immutable releases so a published artifact "
|
|
210
|
+
"cannot change after the fact."
|
|
211
|
+
),
|
|
212
|
+
),
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def category_meta(key: CategoryKey) -> CategoryMeta:
|
|
217
|
+
"""The :class:`CategoryMeta` for ``key`` (registry lookup)."""
|
|
218
|
+
return _CATEGORIES[key]
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def all_categories() -> tuple[CategoryMeta, ...]:
|
|
222
|
+
"""Every category's metadata, in registry (render) order."""
|
|
223
|
+
return tuple(_CATEGORIES.values())
|