secure-code-agent 0.12.6__tar.gz → 0.12.7__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.
Files changed (53) hide show
  1. {secure_code_agent-0.12.6/src/secure_code_agent.egg-info → secure_code_agent-0.12.7}/PKG-INFO +2 -2
  2. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/README.md +1 -1
  3. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7/src/secure_code_agent.egg-info}/PKG-INFO +2 -2
  4. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/__init__.py +1 -1
  5. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/cli.py +12 -1
  6. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/renderers.py +13 -2
  7. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/scoring.py +18 -1
  8. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/LICENSE +0 -0
  9. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/pyproject.toml +0 -0
  10. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/setup.cfg +0 -0
  11. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_agent.egg-info/SOURCES.txt +0 -0
  12. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_agent.egg-info/dependency_links.txt +0 -0
  13. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_agent.egg-info/entry_points.txt +0 -0
  14. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_agent.egg-info/requires.txt +0 -0
  15. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_agent.egg-info/top_level.txt +0 -0
  16. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/baseline.py +0 -0
  17. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/capabilities.py +0 -0
  18. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/config.py +0 -0
  19. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/data/semgrep-offline.yaml +0 -0
  20. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/demo.py +0 -0
  21. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/findings.py +0 -0
  22. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/git_tools.py +0 -0
  23. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/history.py +0 -0
  24. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/instructions.py +0 -0
  25. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/mcp_server.py +0 -0
  26. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/pillar.py +0 -0
  27. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/practice.py +0 -0
  28. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/remediation.py +0 -0
  29. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/ruleset.py +0 -0
  30. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/sarif.py +0 -0
  31. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/scanner_status.py +0 -0
  32. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/scanners/__init__.py +0 -0
  33. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/scanners/bandit_scanner.py +0 -0
  34. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/scanners/base.py +0 -0
  35. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/scanners/builtin_rules.py +0 -0
  36. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/scanners/checkov_scanner.py +0 -0
  37. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/scanners/floor.py +0 -0
  38. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/scanners/gitleaks_scanner.py +0 -0
  39. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/scanners/gosec_scanner.py +0 -0
  40. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/scanners/hadolint_scanner.py +0 -0
  41. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/scanners/njsscan_scanner.py +0 -0
  42. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/scanners/npm_audit_scanner.py +0 -0
  43. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/scanners/osv_scanner.py +0 -0
  44. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/scanners/pip_audit_scanner.py +0 -0
  45. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/scanners/rubocop_scanner.py +0 -0
  46. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/scanners/scorecard_scanner.py +0 -0
  47. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/scanners/semgrep_scanner.py +0 -0
  48. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/scanners/trivy_scanner.py +0 -0
  49. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/scanners/trufflehog_scanner.py +0 -0
  50. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/standards.py +0 -0
  51. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/suppressions.py +0 -0
  52. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/triage.py +0 -0
  53. {secure_code_agent-0.12.6 → secure_code_agent-0.12.7}/src/secure_code_audit/verify.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: secure-code-agent
3
- Version: 0.12.6
3
+ Version: 0.12.7
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
@@ -513,7 +513,7 @@ For agents that support invokable skills, this repo ships a portable skill under
513
513
  ## GitHub Action
514
514
 
515
515
  ```yaml
516
- - uses: marshallguillory86/secure-code-agent@v0.12.6
516
+ - uses: marshallguillory86/secure-code-agent@v0.12.7
517
517
  with:
518
518
  config: secure-code-agent.json
519
519
  fail-on-gate: true
@@ -465,7 +465,7 @@ For agents that support invokable skills, this repo ships a portable skill under
465
465
  ## GitHub Action
466
466
 
467
467
  ```yaml
468
- - uses: marshallguillory86/secure-code-agent@v0.12.6
468
+ - uses: marshallguillory86/secure-code-agent@v0.12.7
469
469
  with:
470
470
  config: secure-code-agent.json
471
471
  fail-on-gate: true
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: secure-code-agent
3
- Version: 0.12.6
3
+ Version: 0.12.7
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
@@ -513,7 +513,7 @@ For agents that support invokable skills, this repo ships a portable skill under
513
513
  ## GitHub Action
514
514
 
515
515
  ```yaml
516
- - uses: marshallguillory86/secure-code-agent@v0.12.6
516
+ - uses: marshallguillory86/secure-code-agent@v0.12.7
517
517
  with:
518
518
  config: secure-code-agent.json
519
519
  fail-on-gate: true
@@ -12,4 +12,4 @@
12
12
  #:
13
13
  #: PyPI is immutable, so 0.4.0 stays wrong. 0.5.0 is the first build whose
14
14
  #: artifacts name their own producer correctly.
15
- __version__ = "0.12.6"
15
+ __version__ = "0.12.7"
@@ -561,7 +561,18 @@ def _do_audit(args: argparse.Namespace) -> int:
561
561
  summarize_axis("test tree", test_findings, test_loc),
562
562
  summarize_axis("documentation", docs_findings, docs_loc or None),
563
563
  summarize_axis("dependencies", dependency_findings),
564
- *(summarize_axis(name, found) for name, found in declared_axes.items()),
564
+ # The note is the reason the project wrote, carried verbatim. A
565
+ # declared axis whose page shows counts and no statement of why is a
566
+ # grade moved in silence — the one thing this mechanism exists not to
567
+ # be (D25).
568
+ *(
569
+ summarize_axis(
570
+ name,
571
+ found,
572
+ note=cfg.capabilities[name.removeprefix(AXIS_PREFIX)],
573
+ )
574
+ for name, found in declared_axes.items()
575
+ ),
565
576
  )
566
577
  # Naming an import on the command line asserts that it contributes coverage,
567
578
  # so a broken one fails the gate even if no config requires that scanner.
@@ -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": _AXIS_NOTES.get(axis.name, ""),
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 = _AXIS_NOTES.get(report.name)
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(name: str, findings: Iterable[Finding], loc: int | None = None) -> AxisReport:
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,