secure-code-agent 0.12.8__tar.gz → 0.12.10__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.8/src/secure_code_agent.egg-info → secure_code_agent-0.12.10}/PKG-INFO +2 -2
  2. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/README.md +1 -1
  3. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10/src/secure_code_agent.egg-info}/PKG-INFO +2 -2
  4. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/__init__.py +1 -1
  5. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/cli.py +8 -0
  6. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/scanners/base.py +16 -1
  7. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/triage.py +20 -1
  8. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/LICENSE +0 -0
  9. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/pyproject.toml +0 -0
  10. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/setup.cfg +0 -0
  11. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_agent.egg-info/SOURCES.txt +0 -0
  12. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_agent.egg-info/dependency_links.txt +0 -0
  13. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_agent.egg-info/entry_points.txt +0 -0
  14. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_agent.egg-info/requires.txt +0 -0
  15. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_agent.egg-info/top_level.txt +0 -0
  16. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/baseline.py +0 -0
  17. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/capabilities.py +0 -0
  18. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/config.py +0 -0
  19. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/data/semgrep-offline.yaml +0 -0
  20. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/demo.py +0 -0
  21. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/findings.py +0 -0
  22. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/git_tools.py +0 -0
  23. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/history.py +0 -0
  24. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/instructions.py +0 -0
  25. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/mcp_server.py +0 -0
  26. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/pillar.py +0 -0
  27. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/practice.py +0 -0
  28. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/remediation.py +0 -0
  29. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/renderers.py +0 -0
  30. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/ruleset.py +0 -0
  31. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/sarif.py +0 -0
  32. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/scanner_status.py +0 -0
  33. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/scanners/__init__.py +0 -0
  34. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/scanners/bandit_scanner.py +0 -0
  35. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/scanners/builtin_rules.py +0 -0
  36. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/scanners/checkov_scanner.py +0 -0
  37. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/scanners/floor.py +0 -0
  38. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/scanners/gitleaks_scanner.py +0 -0
  39. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/scanners/gosec_scanner.py +0 -0
  40. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/scanners/hadolint_scanner.py +0 -0
  41. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/scanners/njsscan_scanner.py +0 -0
  42. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/scanners/npm_audit_scanner.py +0 -0
  43. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/scanners/osv_scanner.py +0 -0
  44. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/scanners/pip_audit_scanner.py +0 -0
  45. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/scanners/rubocop_scanner.py +0 -0
  46. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/scanners/scorecard_scanner.py +0 -0
  47. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/scanners/semgrep_scanner.py +0 -0
  48. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/scanners/trivy_scanner.py +0 -0
  49. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/scanners/trufflehog_scanner.py +0 -0
  50. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/scoring.py +0 -0
  51. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/standards.py +0 -0
  52. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/src/secure_code_audit/suppressions.py +0 -0
  53. {secure_code_agent-0.12.8 → secure_code_agent-0.12.10}/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.8
3
+ Version: 0.12.10
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
@@ -515,7 +515,7 @@ For agents that support invokable skills, this repo ships a portable skill under
515
515
  ## GitHub Action
516
516
 
517
517
  ```yaml
518
- - uses: marshallguillory86/secure-code-agent@v0.12.8
518
+ - uses: marshallguillory86/secure-code-agent@v0.12.10
519
519
  with:
520
520
  config: secure-code-agent.json
521
521
  fail-on-gate: true
@@ -467,7 +467,7 @@ For agents that support invokable skills, this repo ships a portable skill under
467
467
  ## GitHub Action
468
468
 
469
469
  ```yaml
470
- - uses: marshallguillory86/secure-code-agent@v0.12.8
470
+ - uses: marshallguillory86/secure-code-agent@v0.12.10
471
471
  with:
472
472
  config: secure-code-agent.json
473
473
  fail-on-gate: true
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: secure-code-agent
3
- Version: 0.12.8
3
+ Version: 0.12.10
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
@@ -515,7 +515,7 @@ For agents that support invokable skills, this repo ships a portable skill under
515
515
  ## GitHub Action
516
516
 
517
517
  ```yaml
518
- - uses: marshallguillory86/secure-code-agent@v0.12.8
518
+ - uses: marshallguillory86/secure-code-agent@v0.12.10
519
519
  with:
520
520
  config: secure-code-agent.json
521
521
  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.8"
15
+ __version__ = "0.12.10"
@@ -306,6 +306,9 @@ def _do_preflight(args: argparse.Namespace) -> int:
306
306
  "command": " ".join(scanner.command) or None,
307
307
  "version": scanner.binary_version() if available else None,
308
308
  "remedy": None if available else scanner.unavailable_fix_hint(),
309
+ # Disclosed, not silent: the tree asked for a command and was
310
+ # not given it. `--trust-target-config` grants it.
311
+ "ignored_command": scanner.ignored_command,
309
312
  }
310
313
  )
311
314
 
@@ -341,6 +344,11 @@ def _print_preflight(rows: list[dict], unselected: list[str], blocking: list[str
341
344
  print(f" {mark} {row['scanner']:<14} {role:<8} {detail}")
342
345
  if row["remedy"]:
343
346
  print(f" → {row['remedy']}")
347
+ if row.get("ignored_command"):
348
+ print(
349
+ " ! the audited tree's config set this command; ignored "
350
+ "(pass --trust-target-config for a repository you own)"
351
+ )
344
352
  for name in unselected:
345
353
  print(f" ✗ {name:<14} required not enabled in this configuration")
346
354
  # Named, not omitted: a floor tool this run does not evaluate is a stated
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import dataclasses
5
6
  import importlib.util
6
7
  import os
7
8
  import re
@@ -18,6 +19,7 @@ from secure_code_audit.config import (
18
19
  containment_root,
19
20
  is_within,
20
21
  scanner_cfg,
22
+ target_config_is_untrusted,
21
23
  target_executables_allowed,
22
24
  )
23
25
  from secure_code_audit.findings import Category, Confidence, Finding, Severity
@@ -39,6 +41,9 @@ class Scanner(ABC):
39
41
  binary: str # name of the executable on PATH
40
42
  version_flag: str = "--version"
41
43
  python_module: str | None = None
44
+ #: True when the audited tree's own config set this scanner's command and
45
+ #: it was not honoured. See `configure`.
46
+ ignored_command: bool = False
42
47
  default_category: Category = Category.CODE_VULNERABILITIES
43
48
  # How an operator obtains this scanner. Surfaced in the unavailable
44
49
  # finding and in --preflight. The agent never installs anything itself.
@@ -61,7 +66,17 @@ class Scanner(ABC):
61
66
  """
62
67
  root = containment_root(target)
63
68
  self._allow_target_executables = target_executables_allowed(config, target)
64
- self._resolved_command = self._resolve_command(root, self.cfg(config))
69
+ chosen = self.cfg(config)
70
+ # An untrusted config does not choose the command — not just "not an
71
+ # executable from the tree". It could name any program on PATH with
72
+ # any arguments, and `python -c "<code>"` is any program; the
73
+ # `--version` probe ran it during preflight, before anything was
74
+ # audited (maintainability-agent's audit, 2026-10-02). The scanner
75
+ # resolves as it would with no config, and says it ignored one.
76
+ self.ignored_command = bool(chosen.command) and target_config_is_untrusted(config, target)
77
+ if self.ignored_command:
78
+ chosen = dataclasses.replace(chosen, command=[])
79
+ self._resolved_command = self._resolve_command(root, chosen)
65
80
 
66
81
  @property
67
82
  def command(self) -> tuple[str, ...]:
@@ -30,6 +30,7 @@ import re
30
30
  from collections.abc import Iterable
31
31
  from pathlib import Path
32
32
 
33
+ from secure_code_audit.capabilities import AXIS_PREFIX
33
34
  from secure_code_audit.findings import Category, Confidence, Finding, Severity
34
35
 
35
36
 
@@ -75,6 +76,24 @@ LOW_PRECISION: dict[str, str] = {
75
76
  _ACCEPT_AXES = frozenset({"test tree", "documentation"})
76
77
 
77
78
 
79
+ def _is_accepted_axis(axis: str) -> bool:
80
+ """Is this an axis whose findings are not patch targets?
81
+
82
+ The named pair, plus every declared capability. A declaration says the
83
+ finding is the project's architecture being exercised (D24) — the
84
+ scorer already routes it off the code condition and the report already
85
+ lists it as reported-not-scored. Nominating the same finding in §FIX
86
+ asks an agent to change the thing the operator declared.
87
+
88
+ That is what shipped. `capabilities` reached the scorer and the
89
+ renderer and never reached here, so one run said "no findings" in the
90
+ pillar and "68 to fix" in the work order — the same 68, all of them
91
+ `subprocess` calls in a tool whose purpose is running subprocesses
92
+ (D28).
93
+ """
94
+ return axis in _ACCEPT_AXES or axis.startswith(AXIS_PREFIX)
95
+
96
+
78
97
  #: Bandit quotes the offending value: `Possible hardcoded password: 'x'`.
79
98
  _QUOTED_VALUE = re.compile(r"password: '(.*)'\s*$")
80
99
 
@@ -233,7 +252,7 @@ def tier_of(finding: Finding, axis: str = "primary", root: Path | None = None) -
233
252
  genuine hardcoded key in a test fixture is still deliberate, and a
234
253
  high-confidence rule firing there does not make it a patch target.
235
254
  """
236
- if axis in _ACCEPT_AXES:
255
+ if _is_accepted_axis(axis):
237
256
  return Tier.ACCEPT
238
257
  if finding.rule_id in LOW_PRECISION:
239
258
  # Unless the value itself gives it away.