secure-code-agent 0.12.2__tar.gz → 0.12.4__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.2/src/secure_code_agent.egg-info → secure_code_agent-0.12.4}/PKG-INFO +13 -3
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/README.md +12 -2
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4/src/secure_code_agent.egg-info}/PKG-INFO +13 -3
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/__init__.py +1 -1
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/cli.py +22 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/config.py +4 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/git_tools.py +11 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/sarif.py +36 -5
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/suppressions.py +10 -2
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/LICENSE +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/pyproject.toml +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/setup.cfg +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_agent.egg-info/SOURCES.txt +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_agent.egg-info/dependency_links.txt +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_agent.egg-info/entry_points.txt +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_agent.egg-info/requires.txt +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_agent.egg-info/top_level.txt +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/baseline.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/data/semgrep-offline.yaml +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/demo.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/findings.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/history.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/instructions.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/mcp_server.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/pillar.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/practice.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/remediation.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/renderers.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/ruleset.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/scanner_status.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/scanners/__init__.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/scanners/bandit_scanner.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/scanners/base.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/scanners/builtin_rules.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/scanners/checkov_scanner.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/scanners/floor.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/scanners/gitleaks_scanner.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/scanners/gosec_scanner.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/scanners/hadolint_scanner.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/scanners/njsscan_scanner.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/scanners/npm_audit_scanner.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/scanners/osv_scanner.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/scanners/pip_audit_scanner.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/scanners/rubocop_scanner.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/scanners/scorecard_scanner.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/scanners/semgrep_scanner.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/scanners/trivy_scanner.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/scanners/trufflehog_scanner.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/scoring.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/standards.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/triage.py +0 -0
- {secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/verify.py +0 -0
{secure_code_agent-0.12.2/src/secure_code_agent.egg-info → secure_code_agent-0.12.4}/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.4
|
|
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
|
|
@@ -448,6 +448,8 @@ for repositories whose `pyproject.toml` is their authoritative audit input.
|
|
|
448
448
|
expires: "2027-05-13"
|
|
449
449
|
```
|
|
450
450
|
|
|
451
|
+
`paths` entries are repository-relative and use the same pattern syntax as `exclude_patterns`: a trailing `/` means that directory at any depth, a bare name matches at any depth, and `**/` includes the root.
|
|
452
|
+
|
|
451
453
|
Wildcard rule (`rule_id: "*"`) requires a `file` or `paths` scope — you cannot disable a rule globally.
|
|
452
454
|
|
|
453
455
|
## Baseline + incremental adoption
|
|
@@ -511,7 +513,7 @@ For agents that support invokable skills, this repo ships a portable skill under
|
|
|
511
513
|
## GitHub Action
|
|
512
514
|
|
|
513
515
|
```yaml
|
|
514
|
-
- uses: marshallguillory86/secure-code-agent@v0.12.
|
|
516
|
+
- uses: marshallguillory86/secure-code-agent@v0.12.4
|
|
515
517
|
with:
|
|
516
518
|
config: secure-code-agent.json
|
|
517
519
|
fail-on-gate: true
|
|
@@ -521,7 +523,15 @@ The action installs the exact source bundled with the referenced action plus
|
|
|
521
523
|
the pinned `required-scanners` extra (Bandit and pip-audit),
|
|
522
524
|
emits Markdown, JSON, SARIF, PR-comment, and remediation artifacts, and uploads
|
|
523
525
|
SARIF by default. The calling workflow must grant `security-events: write` for
|
|
524
|
-
SARIF upload.
|
|
526
|
+
SARIF upload.
|
|
527
|
+
|
|
528
|
+
What it uploads is `secure-code.code-scanning.sarif`, not the full SARIF. Code
|
|
529
|
+
scanning opens an alert for every uploaded result and does not act on SARIF
|
|
530
|
+
`suppressions`, so uploading the full file raises an alert, and a review thread
|
|
531
|
+
on pull requests, for every test-tree and documentation finding and every
|
|
532
|
+
reviewed `.scignore.yaml` entry. The upload file leaves those out; the full SARIF,
|
|
533
|
+
with each suppression and its reason, is kept as an artifact. Outside the action,
|
|
534
|
+
`--code-scanning-sarif-output PATH` writes the same file. Pin production usage to a full commit SHA; the version tag above
|
|
525
535
|
is shown for readability. See [`action.yml`](action.yml) and
|
|
526
536
|
[`examples/github-actions/`](examples/github-actions/) for full workflows.
|
|
527
537
|
|
|
@@ -400,6 +400,8 @@ for repositories whose `pyproject.toml` is their authoritative audit input.
|
|
|
400
400
|
expires: "2027-05-13"
|
|
401
401
|
```
|
|
402
402
|
|
|
403
|
+
`paths` entries are repository-relative and use the same pattern syntax as `exclude_patterns`: a trailing `/` means that directory at any depth, a bare name matches at any depth, and `**/` includes the root.
|
|
404
|
+
|
|
403
405
|
Wildcard rule (`rule_id: "*"`) requires a `file` or `paths` scope — you cannot disable a rule globally.
|
|
404
406
|
|
|
405
407
|
## Baseline + incremental adoption
|
|
@@ -463,7 +465,7 @@ For agents that support invokable skills, this repo ships a portable skill under
|
|
|
463
465
|
## GitHub Action
|
|
464
466
|
|
|
465
467
|
```yaml
|
|
466
|
-
- uses: marshallguillory86/secure-code-agent@v0.12.
|
|
468
|
+
- uses: marshallguillory86/secure-code-agent@v0.12.4
|
|
467
469
|
with:
|
|
468
470
|
config: secure-code-agent.json
|
|
469
471
|
fail-on-gate: true
|
|
@@ -473,7 +475,15 @@ The action installs the exact source bundled with the referenced action plus
|
|
|
473
475
|
the pinned `required-scanners` extra (Bandit and pip-audit),
|
|
474
476
|
emits Markdown, JSON, SARIF, PR-comment, and remediation artifacts, and uploads
|
|
475
477
|
SARIF by default. The calling workflow must grant `security-events: write` for
|
|
476
|
-
SARIF upload.
|
|
478
|
+
SARIF upload.
|
|
479
|
+
|
|
480
|
+
What it uploads is `secure-code.code-scanning.sarif`, not the full SARIF. Code
|
|
481
|
+
scanning opens an alert for every uploaded result and does not act on SARIF
|
|
482
|
+
`suppressions`, so uploading the full file raises an alert, and a review thread
|
|
483
|
+
on pull requests, for every test-tree and documentation finding and every
|
|
484
|
+
reviewed `.scignore.yaml` entry. The upload file leaves those out; the full SARIF,
|
|
485
|
+
with each suppression and its reason, is kept as an artifact. Outside the action,
|
|
486
|
+
`--code-scanning-sarif-output PATH` writes the same file. Pin production usage to a full commit SHA; the version tag above
|
|
477
487
|
is shown for readability. See [`action.yml`](action.yml) and
|
|
478
488
|
[`examples/github-actions/`](examples/github-actions/) for full workflows.
|
|
479
489
|
|
{secure_code_agent-0.12.2 → secure_code_agent-0.12.4/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.4
|
|
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
|
|
@@ -448,6 +448,8 @@ for repositories whose `pyproject.toml` is their authoritative audit input.
|
|
|
448
448
|
expires: "2027-05-13"
|
|
449
449
|
```
|
|
450
450
|
|
|
451
|
+
`paths` entries are repository-relative and use the same pattern syntax as `exclude_patterns`: a trailing `/` means that directory at any depth, a bare name matches at any depth, and `**/` includes the root.
|
|
452
|
+
|
|
451
453
|
Wildcard rule (`rule_id: "*"`) requires a `file` or `paths` scope — you cannot disable a rule globally.
|
|
452
454
|
|
|
453
455
|
## Baseline + incremental adoption
|
|
@@ -511,7 +513,7 @@ For agents that support invokable skills, this repo ships a portable skill under
|
|
|
511
513
|
## GitHub Action
|
|
512
514
|
|
|
513
515
|
```yaml
|
|
514
|
-
- uses: marshallguillory86/secure-code-agent@v0.12.
|
|
516
|
+
- uses: marshallguillory86/secure-code-agent@v0.12.4
|
|
515
517
|
with:
|
|
516
518
|
config: secure-code-agent.json
|
|
517
519
|
fail-on-gate: true
|
|
@@ -521,7 +523,15 @@ The action installs the exact source bundled with the referenced action plus
|
|
|
521
523
|
the pinned `required-scanners` extra (Bandit and pip-audit),
|
|
522
524
|
emits Markdown, JSON, SARIF, PR-comment, and remediation artifacts, and uploads
|
|
523
525
|
SARIF by default. The calling workflow must grant `security-events: write` for
|
|
524
|
-
SARIF upload.
|
|
526
|
+
SARIF upload.
|
|
527
|
+
|
|
528
|
+
What it uploads is `secure-code.code-scanning.sarif`, not the full SARIF. Code
|
|
529
|
+
scanning opens an alert for every uploaded result and does not act on SARIF
|
|
530
|
+
`suppressions`, so uploading the full file raises an alert, and a review thread
|
|
531
|
+
on pull requests, for every test-tree and documentation finding and every
|
|
532
|
+
reviewed `.scignore.yaml` entry. The upload file leaves those out; the full SARIF,
|
|
533
|
+
with each suppression and its reason, is kept as an artifact. Outside the action,
|
|
534
|
+
`--code-scanning-sarif-output PATH` writes the same file. Pin production usage to a full commit SHA; the version tag above
|
|
525
535
|
is shown for readability. See [`action.yml`](action.yml) and
|
|
526
536
|
[`examples/github-actions/`](examples/github-actions/) for full workflows.
|
|
527
537
|
|
|
@@ -82,6 +82,15 @@ def _parser() -> argparse.ArgumentParser:
|
|
|
82
82
|
p.add_argument("--output", help="Markdown report output path.")
|
|
83
83
|
p.add_argument("--json-output", help="Canonical JSON output path.")
|
|
84
84
|
p.add_argument("--sarif-output", help="SARIF 2.1.0 output path.")
|
|
85
|
+
p.add_argument(
|
|
86
|
+
"--code-scanning-sarif-output",
|
|
87
|
+
help=(
|
|
88
|
+
"SARIF for upload to GitHub code scanning: the --sarif-output results "
|
|
89
|
+
"without the suppressed ones (test tree, documentation, .scignore.yaml). "
|
|
90
|
+
"Code scanning opens an alert for every uploaded result and ignores "
|
|
91
|
+
"SARIF suppressions, so upload this file, not the record."
|
|
92
|
+
),
|
|
93
|
+
)
|
|
85
94
|
p.add_argument("--comment-output", help="PR-comment markdown output path.")
|
|
86
95
|
p.add_argument("--prompt-output", help="Remediation prompt output path.")
|
|
87
96
|
p.add_argument(
|
|
@@ -795,6 +804,7 @@ def _own_artifacts(paths: _OutputPaths, cfg: config_mod.Config, root: Path) -> f
|
|
|
795
804
|
paths.markdown,
|
|
796
805
|
paths.json_out,
|
|
797
806
|
paths.sarif,
|
|
807
|
+
paths.code_scanning_sarif,
|
|
798
808
|
paths.comment,
|
|
799
809
|
paths.prompt,
|
|
800
810
|
paths.security_pillar,
|
|
@@ -933,6 +943,14 @@ def _write_outputs(
|
|
|
933
943
|
renderers.write_json(findings, score, gate, paths.json_out, coverage, verdict, axes, root)
|
|
934
944
|
if paths.sarif is not None:
|
|
935
945
|
sarif.write(findings, paths.sarif, coverage, lambda f: renderers.axis_of(f, axes))
|
|
946
|
+
if paths.code_scanning_sarif is not None:
|
|
947
|
+
sarif.write(
|
|
948
|
+
findings,
|
|
949
|
+
paths.code_scanning_sarif,
|
|
950
|
+
coverage,
|
|
951
|
+
lambda f: renderers.axis_of(f, axes),
|
|
952
|
+
alerts_only=True,
|
|
953
|
+
)
|
|
936
954
|
if paths.comment is not None:
|
|
937
955
|
renderers.write_pr_comment(findings, score, gate, paths.comment, coverage, verdict)
|
|
938
956
|
if paths.prompt is not None:
|
|
@@ -970,6 +988,7 @@ class _OutputPaths:
|
|
|
970
988
|
comment: Path | None
|
|
971
989
|
prompt: Path | None
|
|
972
990
|
security_pillar: Path | None
|
|
991
|
+
code_scanning_sarif: Path | None = None
|
|
973
992
|
|
|
974
993
|
|
|
975
994
|
def _resolve_outputs(args: argparse.Namespace, cfg: config_mod.Config, root: Path) -> _OutputPaths:
|
|
@@ -1014,6 +1033,7 @@ def _resolve_outputs(args: argparse.Namespace, cfg: config_mod.Config, root: Pat
|
|
|
1014
1033
|
comment=_p(args.comment_output, "comment_path"),
|
|
1015
1034
|
prompt=_p(args.prompt_output, "prompt_path"),
|
|
1016
1035
|
security_pillar=_p(args.security_pillar, "security_pillar_path"),
|
|
1036
|
+
code_scanning_sarif=_p(args.code_scanning_sarif_output, "code_scanning_sarif_path"),
|
|
1017
1037
|
)
|
|
1018
1038
|
|
|
1019
1039
|
|
|
@@ -1027,6 +1047,7 @@ _WRITE_KEYS = (
|
|
|
1027
1047
|
"markdown_path",
|
|
1028
1048
|
"json_path",
|
|
1029
1049
|
"sarif_path",
|
|
1050
|
+
"code_scanning_sarif_path",
|
|
1030
1051
|
"comment_path",
|
|
1031
1052
|
"prompt_path",
|
|
1032
1053
|
"security_pillar_path",
|
|
@@ -1169,6 +1190,7 @@ def _print_summary(
|
|
|
1169
1190
|
paths.markdown,
|
|
1170
1191
|
paths.json_out,
|
|
1171
1192
|
paths.sarif,
|
|
1193
|
+
paths.code_scanning_sarif,
|
|
1172
1194
|
paths.comment,
|
|
1173
1195
|
paths.prompt,
|
|
1174
1196
|
paths.security_pillar,
|
|
@@ -136,6 +136,10 @@ DEFAULT_OUTPUTS: dict[str, str] = {
|
|
|
136
136
|
"markdown_path": "secure-code-report.md",
|
|
137
137
|
"json_path": "secure-code-report.json",
|
|
138
138
|
"sarif_path": "secure-code.sarif",
|
|
139
|
+
# For upload to code scanning: the SARIF above without results that carry
|
|
140
|
+
# `suppressions`, which GitHub turns into alerts regardless (D22). Off
|
|
141
|
+
# unless declared, like sarif_path.
|
|
142
|
+
"code_scanning_sarif_path": "secure-code.code-scanning.sarif",
|
|
139
143
|
"comment_path": "secure-code-pr-comment.md",
|
|
140
144
|
"prompt_path": "secure-code-remediation-prompt.md",
|
|
141
145
|
"baseline_path": "secure-code-baseline.json",
|
|
@@ -48,6 +48,17 @@ def is_excluded(path: Path, root: Path, patterns: Iterable[str]) -> bool:
|
|
|
48
48
|
return any(_matches(rel, path.name, pat) for pat in patterns)
|
|
49
49
|
|
|
50
50
|
|
|
51
|
+
def matches_pattern(rel: str, pattern: str) -> bool:
|
|
52
|
+
"""One configured path pattern against one repository-relative POSIX path.
|
|
53
|
+
|
|
54
|
+
The pattern language every path setting shares: `exclude_patterns`,
|
|
55
|
+
`test_patterns`, `docs_patterns`, and `.scignore.yaml` `paths:`. There is
|
|
56
|
+
one of it so the same string cannot mean two things in one configuration
|
|
57
|
+
(D21).
|
|
58
|
+
"""
|
|
59
|
+
return _matches(rel, rel.rsplit("/", 1)[-1], pattern)
|
|
60
|
+
|
|
61
|
+
|
|
51
62
|
def _matches(rel: str, name: str, pat: str) -> bool:
|
|
52
63
|
"""One glob against one path, already made relative to the root.
|
|
53
64
|
|
|
@@ -18,6 +18,7 @@ from secure_code_audit.scanner_status import (
|
|
|
18
18
|
ScannerExecution,
|
|
19
19
|
ScannerOutcome,
|
|
20
20
|
)
|
|
21
|
+
from secure_code_audit.scoring import GATED_FROM_ANY_AXIS
|
|
21
22
|
from secure_code_audit.standards import cwe_url
|
|
22
23
|
|
|
23
24
|
_SARIF_LEVEL = {
|
|
@@ -33,8 +34,25 @@ def emit(
|
|
|
33
34
|
findings: Iterable[Finding],
|
|
34
35
|
coverage: CoverageReport | None = None,
|
|
35
36
|
axis_of=lambda _f: "primary",
|
|
37
|
+
*,
|
|
38
|
+
alerts_only: bool = False,
|
|
36
39
|
) -> dict:
|
|
37
|
-
"""Build a SARIF 2.1.0 document from canonical findings.
|
|
40
|
+
"""Build a SARIF 2.1.0 document from canonical findings.
|
|
41
|
+
|
|
42
|
+
`alerts_only` builds the document for upload to a code-scanning platform:
|
|
43
|
+
results carrying `suppressions` are left out, rather than kept with the
|
|
44
|
+
field. GitHub code scanning opens an alert for every uploaded result and
|
|
45
|
+
does not act on `suppressions`, so the standard form turned each
|
|
46
|
+
test-tree `assert` into a pull-request review thread once D20 made paths
|
|
47
|
+
placeable (D22). The default keeps every result, as the record.
|
|
48
|
+
|
|
49
|
+
**Except what the build fails on.** A side-axis finding in a category the
|
|
50
|
+
gate escalates from any axis — a secret in a test fixture — is marked
|
|
51
|
+
suppressed in the record and still uploaded, without the marker, so the
|
|
52
|
+
Security tab shows the finding that failed the build. An operator's
|
|
53
|
+
reviewed suppression is left out whatever its category; it returns as a
|
|
54
|
+
live critical finding when it expires.
|
|
55
|
+
"""
|
|
38
56
|
findings = list(findings)
|
|
39
57
|
|
|
40
58
|
# Build the rules array per-canonical-rule (rule_id is local; dedupe).
|
|
@@ -42,10 +60,15 @@ def emit(
|
|
|
42
60
|
results: list[dict] = []
|
|
43
61
|
|
|
44
62
|
for f in findings:
|
|
63
|
+
result = _result(f, axis_of(f))
|
|
64
|
+
if alerts_only and result.get("suppressions"):
|
|
65
|
+
if f.suppressed or f.category not in GATED_FROM_ANY_AXIS:
|
|
66
|
+
continue
|
|
67
|
+
del result["suppressions"]
|
|
45
68
|
rid = f.rule_id
|
|
46
69
|
if rid not in rule_meta:
|
|
47
70
|
rule_meta[rid] = _rule(f)
|
|
48
|
-
results.append(
|
|
71
|
+
results.append(result)
|
|
49
72
|
|
|
50
73
|
run = {
|
|
51
74
|
"tool": {
|
|
@@ -144,8 +167,13 @@ def _suppressions(f: Finding, axis: str) -> list[dict] | None:
|
|
|
144
167
|
the shipped source.
|
|
145
168
|
|
|
146
169
|
Suppressed, not omitted. The finding stays in the file with its
|
|
147
|
-
location and justification, so nothing is hidden from a reader
|
|
148
|
-
|
|
170
|
+
location and justification, so nothing is hidden from a reader.
|
|
171
|
+
|
|
172
|
+
**GitHub code scanning does not honour it (D22).** It opens an alert for
|
|
173
|
+
every uploaded result regardless, which this function's premise assumed
|
|
174
|
+
it would not. So this is right for the record, and not enough for the
|
|
175
|
+
upload: `emit(..., alerts_only=True)` leaves these results out of the
|
|
176
|
+
file that goes to code scanning.
|
|
149
177
|
"""
|
|
150
178
|
if f.suppressed:
|
|
151
179
|
return [
|
|
@@ -206,8 +234,11 @@ def write(
|
|
|
206
234
|
output: Path,
|
|
207
235
|
coverage: CoverageReport | None = None,
|
|
208
236
|
axis_of=lambda _f: "primary",
|
|
237
|
+
*,
|
|
238
|
+
alerts_only: bool = False,
|
|
209
239
|
) -> None:
|
|
210
|
-
|
|
240
|
+
document = emit(findings, coverage, axis_of, alerts_only=alerts_only)
|
|
241
|
+
output.write_text(json.dumps(document, indent=2), encoding="utf-8")
|
|
211
242
|
|
|
212
243
|
|
|
213
244
|
# ----- ingest -------------------------------------------------------------
|
|
@@ -17,7 +17,7 @@ Entry schema (.scignore.yaml)::
|
|
|
17
17
|
...
|
|
18
18
|
expires: 2027-08-01 # required; max 365 days out
|
|
19
19
|
file: api/tests/x.py # optional path (suffix match)
|
|
20
|
-
paths: ["api
|
|
20
|
+
paths: ["api/"] # optional; exclude_patterns syntax, repository-relative
|
|
21
21
|
fingerprint: 0aaa689f8a967d8c # optional; 16 hex chars, from the report
|
|
22
22
|
line: 18 # optional; positive integer
|
|
23
23
|
|
|
@@ -37,6 +37,7 @@ from dataclasses import dataclass, field
|
|
|
37
37
|
from pathlib import Path
|
|
38
38
|
|
|
39
39
|
from secure_code_audit.findings import Category, Confidence, Finding, Severity
|
|
40
|
+
from secure_code_audit.git_tools import matches_pattern
|
|
40
41
|
|
|
41
42
|
_DATE_RE = re.compile(r"^\d{4}-\d{2}-\d{2}$")
|
|
42
43
|
_MAX_TTL_DAYS = 365
|
|
@@ -77,6 +78,13 @@ class SuppressionRule:
|
|
|
77
78
|
against the path with a leading `/`, and `*/src/app.py` matches
|
|
78
79
|
`/src/app.py`. `root` lets an absolute `file:` and a fingerprint
|
|
79
80
|
recorded by an earlier release keep matching the finding they named.
|
|
81
|
+
|
|
82
|
+
**A `paths:` glob is an exclude pattern (D21).** It is matched by
|
|
83
|
+
`git_tools.matches_pattern`, the matcher `exclude_patterns` uses, so a
|
|
84
|
+
trailing slash means "this directory, at any depth" and a bare name
|
|
85
|
+
matches at any depth. Through 0.12.2 it was bare `fnmatch`, under which
|
|
86
|
+
`paths: ["tests/"]` — the example README, `design.md` and the skill all
|
|
87
|
+
show — matched nothing.
|
|
80
88
|
"""
|
|
81
89
|
if self.rule_id != "*" and self.rule_id != finding.rule_id:
|
|
82
90
|
return False
|
|
@@ -88,7 +96,7 @@ class SuppressionRule:
|
|
|
88
96
|
if self.line is not None and self.line != finding.line_start:
|
|
89
97
|
return False
|
|
90
98
|
return not self.paths or any(
|
|
91
|
-
|
|
99
|
+
matches_pattern(rel, p) or fnmatch.fnmatch(f"/{rel}", p) for p in self.paths
|
|
92
100
|
)
|
|
93
101
|
|
|
94
102
|
def _names_file(self, rel: str, finding: Finding, root: Path | None) -> bool:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_agent.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_agent.egg-info/requires.txt
RENAMED
|
File without changes
|
{secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/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
|
{secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/scanner_status.py
RENAMED
|
File without changes
|
{secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/scanners/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/src/secure_code_audit/scanners/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{secure_code_agent-0.12.2 → secure_code_agent-0.12.4}/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.2 → secure_code_agent-0.12.4}/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
|