secure-code-agent 0.12.6__tar.gz → 0.12.8__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.
- {secure_code_agent-0.12.6/src/secure_code_agent.egg-info → secure_code_agent-0.12.8}/PKG-INFO +6 -4
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/README.md +5 -3
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8/src/secure_code_agent.egg-info}/PKG-INFO +6 -4
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/__init__.py +1 -1
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/cli.py +41 -1
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/config.py +6 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/remediation.py +88 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/renderers.py +13 -2
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scoring.py +18 -1
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/LICENSE +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/pyproject.toml +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/setup.cfg +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_agent.egg-info/SOURCES.txt +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_agent.egg-info/dependency_links.txt +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_agent.egg-info/entry_points.txt +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_agent.egg-info/requires.txt +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_agent.egg-info/top_level.txt +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/baseline.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/capabilities.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/data/semgrep-offline.yaml +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/demo.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/findings.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/git_tools.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/history.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/instructions.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/mcp_server.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/pillar.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/practice.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/ruleset.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/sarif.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scanner_status.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scanners/__init__.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scanners/bandit_scanner.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scanners/base.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scanners/builtin_rules.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scanners/checkov_scanner.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scanners/floor.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scanners/gitleaks_scanner.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scanners/gosec_scanner.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scanners/hadolint_scanner.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scanners/njsscan_scanner.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scanners/npm_audit_scanner.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scanners/osv_scanner.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scanners/pip_audit_scanner.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scanners/rubocop_scanner.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scanners/scorecard_scanner.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scanners/semgrep_scanner.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scanners/trivy_scanner.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scanners/trufflehog_scanner.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/standards.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/suppressions.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/triage.py +0 -0
- {secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/verify.py +0 -0
{secure_code_agent-0.12.6/src/secure_code_agent.egg-info → secure_code_agent-0.12.8}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: secure-code-agent
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.8
|
|
4
4
|
Summary: Deterministic security gate + bounded AI remediation prompt generator. NIST SSDF / OWASP ASVS / CWE Top 25 anchored.
|
|
5
5
|
Author: Marshall Guillory
|
|
6
6
|
License: MIT
|
|
@@ -60,7 +60,9 @@ secure-code-agent --fail-on-gate \
|
|
|
60
60
|
--sarif-output secure-code.sarif
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
**Runs on its own.** Install it, point it at a repository, get the gate, the report and the work order. Nothing else is required, and most people who use it will use only this.
|
|
64
|
+
|
|
65
|
+
It is also the sibling of [`maintainability-agent`](https://github.com/marshallguillory86/maintainability-agent) — same shape (deterministic CI gate · plain-file outputs · per-host skill bundle), different concern — and can feed that tool's Security pillar through [one file](docs/ma-integration.md). That is an integration, not a dependency: a family resemblance, not a component relationship. The two are separate products, installed separately, with their own dependency pins.
|
|
64
66
|
|
|
65
67
|
---
|
|
66
68
|
|
|
@@ -513,7 +515,7 @@ For agents that support invokable skills, this repo ships a portable skill under
|
|
|
513
515
|
## GitHub Action
|
|
514
516
|
|
|
515
517
|
```yaml
|
|
516
|
-
- uses: marshallguillory86/secure-code-agent@v0.12.
|
|
518
|
+
- uses: marshallguillory86/secure-code-agent@v0.12.8
|
|
517
519
|
with:
|
|
518
520
|
config: secure-code-agent.json
|
|
519
521
|
fail-on-gate: true
|
|
@@ -612,4 +614,4 @@ MIT — see [`LICENSE`](LICENSE).
|
|
|
612
614
|
|
|
613
615
|
---
|
|
614
616
|
|
|
615
|
-
Built by [Marshall Guillory](https://github.com/marshallguillory86)
|
|
617
|
+
Built by [Marshall Guillory](https://github.com/marshallguillory86), who also builds [`maintainability-agent`](https://github.com/marshallguillory86/maintainability-agent). Two independent tools, released and installed separately, encoding one thesis: *AI agents need deterministic boundaries, not best-effort guardrails.*
|
|
@@ -12,7 +12,9 @@ secure-code-agent --fail-on-gate \
|
|
|
12
12
|
--sarif-output secure-code.sarif
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
**Runs on its own.** Install it, point it at a repository, get the gate, the report and the work order. Nothing else is required, and most people who use it will use only this.
|
|
16
|
+
|
|
17
|
+
It is also the sibling of [`maintainability-agent`](https://github.com/marshallguillory86/maintainability-agent) — same shape (deterministic CI gate · plain-file outputs · per-host skill bundle), different concern — and can feed that tool's Security pillar through [one file](docs/ma-integration.md). That is an integration, not a dependency: a family resemblance, not a component relationship. The two are separate products, installed separately, with their own dependency pins.
|
|
16
18
|
|
|
17
19
|
---
|
|
18
20
|
|
|
@@ -465,7 +467,7 @@ For agents that support invokable skills, this repo ships a portable skill under
|
|
|
465
467
|
## GitHub Action
|
|
466
468
|
|
|
467
469
|
```yaml
|
|
468
|
-
- uses: marshallguillory86/secure-code-agent@v0.12.
|
|
470
|
+
- uses: marshallguillory86/secure-code-agent@v0.12.8
|
|
469
471
|
with:
|
|
470
472
|
config: secure-code-agent.json
|
|
471
473
|
fail-on-gate: true
|
|
@@ -564,4 +566,4 @@ MIT — see [`LICENSE`](LICENSE).
|
|
|
564
566
|
|
|
565
567
|
---
|
|
566
568
|
|
|
567
|
-
Built by [Marshall Guillory](https://github.com/marshallguillory86)
|
|
569
|
+
Built by [Marshall Guillory](https://github.com/marshallguillory86), who also builds [`maintainability-agent`](https://github.com/marshallguillory86/maintainability-agent). Two independent tools, released and installed separately, encoding one thesis: *AI agents need deterministic boundaries, not best-effort guardrails.*
|
{secure_code_agent-0.12.6 → secure_code_agent-0.12.8/src/secure_code_agent.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: secure-code-agent
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.8
|
|
4
4
|
Summary: Deterministic security gate + bounded AI remediation prompt generator. NIST SSDF / OWASP ASVS / CWE Top 25 anchored.
|
|
5
5
|
Author: Marshall Guillory
|
|
6
6
|
License: MIT
|
|
@@ -60,7 +60,9 @@ secure-code-agent --fail-on-gate \
|
|
|
60
60
|
--sarif-output secure-code.sarif
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
**Runs on its own.** Install it, point it at a repository, get the gate, the report and the work order. Nothing else is required, and most people who use it will use only this.
|
|
64
|
+
|
|
65
|
+
It is also the sibling of [`maintainability-agent`](https://github.com/marshallguillory86/maintainability-agent) — same shape (deterministic CI gate · plain-file outputs · per-host skill bundle), different concern — and can feed that tool's Security pillar through [one file](docs/ma-integration.md). That is an integration, not a dependency: a family resemblance, not a component relationship. The two are separate products, installed separately, with their own dependency pins.
|
|
64
66
|
|
|
65
67
|
---
|
|
66
68
|
|
|
@@ -513,7 +515,7 @@ For agents that support invokable skills, this repo ships a portable skill under
|
|
|
513
515
|
## GitHub Action
|
|
514
516
|
|
|
515
517
|
```yaml
|
|
516
|
-
- uses: marshallguillory86/secure-code-agent@v0.12.
|
|
518
|
+
- uses: marshallguillory86/secure-code-agent@v0.12.8
|
|
517
519
|
with:
|
|
518
520
|
config: secure-code-agent.json
|
|
519
521
|
fail-on-gate: true
|
|
@@ -612,4 +614,4 @@ MIT — see [`LICENSE`](LICENSE).
|
|
|
612
614
|
|
|
613
615
|
---
|
|
614
616
|
|
|
615
|
-
Built by [Marshall Guillory](https://github.com/marshallguillory86)
|
|
617
|
+
Built by [Marshall Guillory](https://github.com/marshallguillory86), who also builds [`maintainability-agent`](https://github.com/marshallguillory86/maintainability-agent). Two independent tools, released and installed separately, encoding one thesis: *AI agents need deterministic boundaries, not best-effort guardrails.*
|
|
@@ -98,6 +98,14 @@ def _parser() -> argparse.ArgumentParser:
|
|
|
98
98
|
)
|
|
99
99
|
p.add_argument("--comment-output", help="PR-comment markdown output path.")
|
|
100
100
|
p.add_argument("--prompt-output", help="Remediation prompt output path.")
|
|
101
|
+
p.add_argument(
|
|
102
|
+
"--work-order-json",
|
|
103
|
+
help=(
|
|
104
|
+
"The remediation work order as JSON: the same triage and the same "
|
|
105
|
+
"caps as the prompt, as facts rather than prose, for a tool that "
|
|
106
|
+
"renders its own presentation."
|
|
107
|
+
),
|
|
108
|
+
)
|
|
101
109
|
p.add_argument(
|
|
102
110
|
"--security-pillar",
|
|
103
111
|
help=(
|
|
@@ -561,7 +569,18 @@ def _do_audit(args: argparse.Namespace) -> int:
|
|
|
561
569
|
summarize_axis("test tree", test_findings, test_loc),
|
|
562
570
|
summarize_axis("documentation", docs_findings, docs_loc or None),
|
|
563
571
|
summarize_axis("dependencies", dependency_findings),
|
|
564
|
-
|
|
572
|
+
# The note is the reason the project wrote, carried verbatim. A
|
|
573
|
+
# declared axis whose page shows counts and no statement of why is a
|
|
574
|
+
# grade moved in silence — the one thing this mechanism exists not to
|
|
575
|
+
# be (D25).
|
|
576
|
+
*(
|
|
577
|
+
summarize_axis(
|
|
578
|
+
name,
|
|
579
|
+
found,
|
|
580
|
+
note=cfg.capabilities[name.removeprefix(AXIS_PREFIX)],
|
|
581
|
+
)
|
|
582
|
+
for name, found in declared_axes.items()
|
|
583
|
+
),
|
|
565
584
|
)
|
|
566
585
|
# Naming an import on the command line asserts that it contributes coverage,
|
|
567
586
|
# so a broken one fails the gate even if no config requires that scanner.
|
|
@@ -867,6 +886,10 @@ def _own_artifacts(paths: _OutputPaths, cfg: config_mod.Config, root: Path) -> f
|
|
|
867
886
|
paths.json_out,
|
|
868
887
|
paths.sarif,
|
|
869
888
|
paths.code_scanning_sarif,
|
|
889
|
+
# The work order as data (D27). Here for the same reason the prompt
|
|
890
|
+
# is: it quotes finding text and code snippets, so the next run
|
|
891
|
+
# would score this tool's own output as source.
|
|
892
|
+
paths.work_order_json,
|
|
870
893
|
paths.comment,
|
|
871
894
|
paths.prompt,
|
|
872
895
|
paths.security_pillar,
|
|
@@ -1019,6 +1042,20 @@ def _write_outputs(
|
|
|
1019
1042
|
# The work order needs the axis to tier a finding: a secret in a
|
|
1020
1043
|
# test fixture is a suppression decision, not a patch target.
|
|
1021
1044
|
remediation.write(findings, paths.prompt, root, lambda f: renderers.axis_of(f, axes))
|
|
1045
|
+
# The same work order as facts. `maintainability-agent` embeds this one
|
|
1046
|
+
# in an HTML report, and prose is the thing a consumer cannot
|
|
1047
|
+
# re-present: with only Markdown it wrapped the text in `<pre>` and a
|
|
1048
|
+
# reader got `##` headings inside a rendered page (D27).
|
|
1049
|
+
if paths.work_order_json is not None:
|
|
1050
|
+
paths.work_order_json.parent.mkdir(parents=True, exist_ok=True)
|
|
1051
|
+
paths.work_order_json.write_text(
|
|
1052
|
+
json.dumps(
|
|
1053
|
+
remediation.as_data(findings, root, lambda f: renderers.axis_of(f, axes)),
|
|
1054
|
+
indent=2,
|
|
1055
|
+
)
|
|
1056
|
+
+ "\n",
|
|
1057
|
+
encoding="utf-8",
|
|
1058
|
+
)
|
|
1022
1059
|
# The artifact maintainability-agent ingests (D3). Written last because it
|
|
1023
1060
|
# is the only output that carries both axes plus the practice level.
|
|
1024
1061
|
if paths.security_pillar is not None and pillar is not None:
|
|
@@ -1051,6 +1088,8 @@ class _OutputPaths:
|
|
|
1051
1088
|
prompt: Path | None
|
|
1052
1089
|
security_pillar: Path | None
|
|
1053
1090
|
code_scanning_sarif: Path | None = None
|
|
1091
|
+
#: The work order as data, for a tool that renders its own (D27).
|
|
1092
|
+
work_order_json: Path | None = None
|
|
1054
1093
|
|
|
1055
1094
|
|
|
1056
1095
|
def _resolve_outputs(args: argparse.Namespace, cfg: config_mod.Config, root: Path) -> _OutputPaths:
|
|
@@ -1096,6 +1135,7 @@ def _resolve_outputs(args: argparse.Namespace, cfg: config_mod.Config, root: Pat
|
|
|
1096
1135
|
prompt=_p(args.prompt_output, "prompt_path"),
|
|
1097
1136
|
security_pillar=_p(args.security_pillar, "security_pillar_path"),
|
|
1098
1137
|
code_scanning_sarif=_p(args.code_scanning_sarif_output, "code_scanning_sarif_path"),
|
|
1138
|
+
work_order_json=_p(args.work_order_json, "work_order_json_path"),
|
|
1099
1139
|
)
|
|
1100
1140
|
|
|
1101
1141
|
|
|
@@ -147,6 +147,12 @@ DEFAULT_OUTPUTS: dict[str, str] = {
|
|
|
147
147
|
"code_scanning_sarif_path": "secure-code.code-scanning.sarif",
|
|
148
148
|
"comment_path": "secure-code-pr-comment.md",
|
|
149
149
|
"prompt_path": "secure-code-remediation-prompt.md",
|
|
150
|
+
# The work order as data, for a tool that renders its own presentation
|
|
151
|
+
# (D27). Off unless declared, like the code-scanning SARIF: the prompt
|
|
152
|
+
# is the artifact an operator reads, and a second copy in JSON is only
|
|
153
|
+
# wanted by a consumer. The name is still declared so this tool never
|
|
154
|
+
# scans a file it wrote.
|
|
155
|
+
"work_order_json_path": "secure-code-work-order.json",
|
|
150
156
|
"baseline_path": "secure-code-baseline.json",
|
|
151
157
|
# Append-only trend. The score's one genuine use is movement over time.
|
|
152
158
|
"history_path": ".secure-code/history.jsonl",
|
|
@@ -10,6 +10,7 @@ from __future__ import annotations
|
|
|
10
10
|
|
|
11
11
|
from collections.abc import Iterable
|
|
12
12
|
from pathlib import Path
|
|
13
|
+
from typing import Any
|
|
13
14
|
|
|
14
15
|
from secure_code_audit import triage
|
|
15
16
|
from secure_code_audit.findings import Finding
|
|
@@ -245,6 +246,93 @@ def generate(
|
|
|
245
246
|
return "\n".join(parts)
|
|
246
247
|
|
|
247
248
|
|
|
249
|
+
#: The work order's data schema. Bumped when a consumer would have to
|
|
250
|
+
#: change to keep reading it; new optional keys do not bump it.
|
|
251
|
+
WORK_ORDER_SCHEMA_VERSION = 1
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _finding_data(f: Finding, root: Path | None, note: str | None = None) -> dict[str, Any]:
|
|
255
|
+
"""One finding, as the facts a renderer needs and nothing more.
|
|
256
|
+
|
|
257
|
+
Deliberately not the whole `Finding`. The fingerprint, the baseline
|
|
258
|
+
flag and the suppression note are this tool's bookkeeping; a consumer
|
|
259
|
+
drawing a work order needs what it is, where it is, what to do, and
|
|
260
|
+
the citation that makes it checkable.
|
|
261
|
+
"""
|
|
262
|
+
return {
|
|
263
|
+
"rule_id": f.rule_id,
|
|
264
|
+
"scanner": f.scanner,
|
|
265
|
+
"title": f.short_desc or f.message,
|
|
266
|
+
"message": f.message,
|
|
267
|
+
"fix_hint": f.fix_hint,
|
|
268
|
+
"severity": f.severity.value,
|
|
269
|
+
"confidence": f.confidence.value,
|
|
270
|
+
"category": f.category.value,
|
|
271
|
+
"path": _display_path(f.file_path, root),
|
|
272
|
+
"line_start": f.line_start,
|
|
273
|
+
"line_end": f.line_end,
|
|
274
|
+
"location": _location(f, root),
|
|
275
|
+
"code_snippet": f.code_snippet,
|
|
276
|
+
"standards": {
|
|
277
|
+
"cwe": f.canonical_cwe,
|
|
278
|
+
"cwe_top25": f.cwe_top25,
|
|
279
|
+
"owasp_top10": f.owasp_top10,
|
|
280
|
+
"asvs_section": f.asvs_section,
|
|
281
|
+
"nist_ssdf": f.nist_ssdf,
|
|
282
|
+
},
|
|
283
|
+
# Only §REVIEW findings carry one: it is why the tier demoted them.
|
|
284
|
+
"review_note": note,
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def as_data(
|
|
289
|
+
findings: Iterable[Finding],
|
|
290
|
+
root: Path | None = None,
|
|
291
|
+
axis_of=lambda _f: "primary",
|
|
292
|
+
) -> dict[str, Any]:
|
|
293
|
+
"""The work order as facts, for a consumer that renders its own.
|
|
294
|
+
|
|
295
|
+
`generate` writes Markdown, which is right for the operator and for
|
|
296
|
+
the agent that reads the prompt. It is the wrong thing to hand a
|
|
297
|
+
*tool*: `maintainability-agent` embeds this work order in an HTML
|
|
298
|
+
report and had only prose, so it wrapped it in `<pre>` and a reader
|
|
299
|
+
got raw Markdown — headings as `##`, bold as asterisks — inside a
|
|
300
|
+
page where everything else was rendered.
|
|
301
|
+
|
|
302
|
+
Prose is the one thing a consumer cannot re-present. This is the same
|
|
303
|
+
triage, the same order and the same caps, as data: the tiers come from
|
|
304
|
+
`triage.partition` exactly as the Markdown's do, so the two can never
|
|
305
|
+
describe different work.
|
|
306
|
+
|
|
307
|
+
The caps are kept rather than dropped. A consumer showing more than
|
|
308
|
+
the prompt shows would disagree with the artifact the operator reads,
|
|
309
|
+
and `omitted` says how many are not here — the same number
|
|
310
|
+
`_overflow` prints.
|
|
311
|
+
"""
|
|
312
|
+
tiers = triage.partition(findings, axis_of, root)
|
|
313
|
+
fix = tiers[triage.Tier.FIX]
|
|
314
|
+
review = tiers[triage.Tier.REVIEW]
|
|
315
|
+
accept = tiers[triage.Tier.ACCEPT]
|
|
316
|
+
return {
|
|
317
|
+
"schema_version": WORK_ORDER_SCHEMA_VERSION,
|
|
318
|
+
"counts": {"fix": len(fix), "review": len(review), "accept": len(accept)},
|
|
319
|
+
"fix": {
|
|
320
|
+
"shown": [_finding_data(f, root) for f in fix[:_MAX_BLOCKS]],
|
|
321
|
+
"omitted": max(0, len(fix) - _MAX_BLOCKS),
|
|
322
|
+
},
|
|
323
|
+
"review": {
|
|
324
|
+
"shown": [
|
|
325
|
+
_finding_data(f, root, triage.reason_for(f, root))
|
|
326
|
+
for f in review[:_MAX_REVIEW_LINES]
|
|
327
|
+
],
|
|
328
|
+
"omitted": max(0, len(review) - _MAX_REVIEW_LINES),
|
|
329
|
+
},
|
|
330
|
+
# The accept tier is a count and a summary in the Markdown too: it is
|
|
331
|
+
# the test tree and documentation, which nobody patches one by one.
|
|
332
|
+
"accept": {"summary": _accept_summary(accept, root) if accept else ""},
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
|
|
248
336
|
def _location(f: Finding, root: Path | None) -> str:
|
|
249
337
|
span = f"-{f.line_end}" if f.line_end and f.line_end != f.line_start else ""
|
|
250
338
|
return f"{_display_path(f.file_path, root)}:{f.line_start}{span}"
|
|
@@ -167,7 +167,7 @@ def _axes_to_dict(axes: Iterable[AxisReport]) -> dict:
|
|
|
167
167
|
"count": axis.count,
|
|
168
168
|
"scored": False,
|
|
169
169
|
"gated": axis.name == "dependencies",
|
|
170
|
-
"note":
|
|
170
|
+
"note": _axis_note(axis),
|
|
171
171
|
"per_severity_count": {s.value: n for s, n in axis.per_severity_count.items()},
|
|
172
172
|
"per_category_count": {c.value: n for c, n in axis.per_category_count.items()},
|
|
173
173
|
# Not repeated here: every one of these appears in the top-level
|
|
@@ -206,6 +206,17 @@ _AXIS_NOTES = {
|
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
|
|
209
|
+
def _axis_note(axis: AxisReport) -> str:
|
|
210
|
+
"""Why this axis sits outside the score, in the report's own words.
|
|
211
|
+
|
|
212
|
+
An axis that carries its own note wins. Only a declared axis does, and
|
|
213
|
+
its note is the reason the audited project wrote in its configuration —
|
|
214
|
+
the tool cannot supply that sentence, and a declared axis printed without
|
|
215
|
+
it is a grade moved with no statement of why.
|
|
216
|
+
"""
|
|
217
|
+
return axis.note or _AXIS_NOTES.get(axis.name, "")
|
|
218
|
+
|
|
219
|
+
|
|
209
220
|
def write_json(
|
|
210
221
|
findings: Iterable[Finding],
|
|
211
222
|
score: ScoreReport,
|
|
@@ -361,7 +372,7 @@ def _axis_section(report: AxisReport | None) -> str:
|
|
|
361
372
|
)
|
|
362
373
|
)
|
|
363
374
|
out.append(f"- **By severity:** {by_severity}")
|
|
364
|
-
note =
|
|
375
|
+
note = _axis_note(report)
|
|
365
376
|
if note:
|
|
366
377
|
out.append(f"- {note}")
|
|
367
378
|
out.append("")
|
|
@@ -554,6 +554,17 @@ class AxisReport:
|
|
|
554
554
|
#: advisories are counted against a lockfile, not a line count, so this is
|
|
555
555
|
#: None for them rather than a misleading zero.
|
|
556
556
|
loc: int | None = None
|
|
557
|
+
#: Why this axis sits outside the score, in the report's own words. Empty
|
|
558
|
+
#: for the fixed axes, whose notes the renderer holds because they are the
|
|
559
|
+
#: same sentence for every project.
|
|
560
|
+
#:
|
|
561
|
+
#: A declared axis is the case this field exists for. Its justification is
|
|
562
|
+
#: not a constant the tool can write: it is the reason *this* project
|
|
563
|
+
#: stated in its configuration. 0.12.6 shipped the declaration reaching the
|
|
564
|
+
#: routing and never reaching the page, so a reader saw
|
|
565
|
+
#: `## Declared: spawns_processes` with counts under it and no statement of
|
|
566
|
+
#: why — which is the disclosure the whole mechanism rests on (D25).
|
|
567
|
+
note: str = ""
|
|
557
568
|
|
|
558
569
|
@property
|
|
559
570
|
def count(self) -> int:
|
|
@@ -580,7 +591,12 @@ class AxisReport:
|
|
|
580
591
|
)
|
|
581
592
|
|
|
582
593
|
|
|
583
|
-
def summarize_axis(
|
|
594
|
+
def summarize_axis(
|
|
595
|
+
name: str,
|
|
596
|
+
findings: Iterable[Finding],
|
|
597
|
+
loc: int | None = None,
|
|
598
|
+
note: str = "",
|
|
599
|
+
) -> AxisReport:
|
|
584
600
|
"""Count an axis without scoring it.
|
|
585
601
|
|
|
586
602
|
Not named `test_*` anything: pytest collects any callable whose name begins
|
|
@@ -601,6 +617,7 @@ def summarize_axis(name: str, findings: Iterable[Finding], loc: int | None = Non
|
|
|
601
617
|
return AxisReport(
|
|
602
618
|
name=name,
|
|
603
619
|
loc=loc,
|
|
620
|
+
note=note,
|
|
604
621
|
findings=findings,
|
|
605
622
|
per_severity_count=per_severity,
|
|
606
623
|
per_category_count=per_category,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_agent.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_agent.egg-info/requires.txt
RENAMED
|
File without changes
|
{secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_agent.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scanner_status.py
RENAMED
|
File without changes
|
{secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scanners/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scanners/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scanners/floor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{secure_code_agent-0.12.6 → secure_code_agent-0.12.8}/src/secure_code_audit/scanners/osv_scanner.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|