secure-code-agent 0.2.0__tar.gz → 0.3.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.
Files changed (53) hide show
  1. {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/PKG-INFO +109 -18
  2. secure_code_agent-0.2.0/src/secure_code_agent.egg-info/PKG-INFO → secure_code_agent-0.3.0/README.md +96 -50
  3. {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/pyproject.toml +32 -4
  4. secure_code_agent-0.2.0/README.md → secure_code_agent-0.3.0/src/secure_code_agent.egg-info/PKG-INFO +141 -16
  5. {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_agent.egg-info/SOURCES.txt +1 -0
  6. secure_code_agent-0.3.0/src/secure_code_agent.egg-info/requires.txt +16 -0
  7. {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/__init__.py +1 -1
  8. {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/baseline.py +37 -28
  9. secure_code_agent-0.3.0/src/secure_code_audit/cli.py +515 -0
  10. secure_code_agent-0.3.0/src/secure_code_audit/config.py +264 -0
  11. {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/findings.py +66 -66
  12. {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/git_tools.py +3 -20
  13. {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/instructions.py +17 -18
  14. {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/remediation.py +2 -2
  15. secure_code_agent-0.3.0/src/secure_code_audit/renderers.py +364 -0
  16. secure_code_agent-0.3.0/src/secure_code_audit/sarif.py +363 -0
  17. secure_code_agent-0.3.0/src/secure_code_audit/scanner_status.py +169 -0
  18. secure_code_agent-0.3.0/src/secure_code_audit/scanners/__init__.py +47 -0
  19. secure_code_agent-0.3.0/src/secure_code_audit/scanners/bandit_scanner.py +121 -0
  20. {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/scanners/base.py +85 -32
  21. {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/scanners/builtin_rules.py +22 -18
  22. secure_code_agent-0.3.0/src/secure_code_audit/scanners/checkov_scanner.py +105 -0
  23. secure_code_agent-0.3.0/src/secure_code_audit/scanners/gitleaks_scanner.py +112 -0
  24. secure_code_agent-0.3.0/src/secure_code_audit/scanners/hadolint_scanner.py +159 -0
  25. secure_code_agent-0.3.0/src/secure_code_audit/scanners/npm_audit_scanner.py +158 -0
  26. secure_code_agent-0.3.0/src/secure_code_audit/scanners/osv_scanner.py +159 -0
  27. secure_code_agent-0.3.0/src/secure_code_audit/scanners/pip_audit_scanner.py +233 -0
  28. {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/scanners/scorecard_scanner.py +101 -42
  29. secure_code_agent-0.3.0/src/secure_code_audit/scanners/semgrep_scanner.py +156 -0
  30. {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/scanners/trivy_scanner.py +56 -17
  31. {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/scanners/trufflehog_scanner.py +55 -14
  32. {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/scoring.py +74 -59
  33. {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/standards.py +279 -147
  34. {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/suppressions.py +53 -50
  35. secure_code_agent-0.2.0/src/secure_code_agent.egg-info/requires.txt +0 -5
  36. secure_code_agent-0.2.0/src/secure_code_audit/cli.py +0 -258
  37. secure_code_agent-0.2.0/src/secure_code_audit/config.py +0 -115
  38. secure_code_agent-0.2.0/src/secure_code_audit/renderers.py +0 -253
  39. secure_code_agent-0.2.0/src/secure_code_audit/sarif.py +0 -221
  40. secure_code_agent-0.2.0/src/secure_code_audit/scanners/__init__.py +0 -50
  41. secure_code_agent-0.2.0/src/secure_code_audit/scanners/bandit_scanner.py +0 -83
  42. secure_code_agent-0.2.0/src/secure_code_audit/scanners/checkov_scanner.py +0 -69
  43. secure_code_agent-0.2.0/src/secure_code_audit/scanners/gitleaks_scanner.py +0 -86
  44. secure_code_agent-0.2.0/src/secure_code_audit/scanners/hadolint_scanner.py +0 -107
  45. secure_code_agent-0.2.0/src/secure_code_audit/scanners/npm_audit_scanner.py +0 -101
  46. secure_code_agent-0.2.0/src/secure_code_audit/scanners/osv_scanner.py +0 -108
  47. secure_code_agent-0.2.0/src/secure_code_audit/scanners/pip_audit_scanner.py +0 -83
  48. secure_code_agent-0.2.0/src/secure_code_audit/scanners/semgrep_scanner.py +0 -119
  49. {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/LICENSE +0 -0
  50. {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/setup.cfg +0 -0
  51. {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_agent.egg-info/dependency_links.txt +0 -0
  52. {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_agent.egg-info/entry_points.txt +0 -0
  53. {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_agent.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: secure-code-agent
3
- Version: 0.2.0
3
+ Version: 0.3.0
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
@@ -21,15 +21,26 @@ Classifier: Programming Language :: Python :: 3
21
21
  Classifier: Programming Language :: Python :: 3.10
22
22
  Classifier: Programming Language :: Python :: 3.11
23
23
  Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Programming Language :: Python :: 3.14
24
26
  Classifier: Topic :: Security
25
27
  Classifier: Topic :: Software Development :: Quality Assurance
26
28
  Requires-Python: >=3.10
27
29
  Description-Content-Type: text/markdown
28
30
  License-File: LICENSE
31
+ Requires-Dist: PyYAML<7,>=6.0.2
32
+ Provides-Extra: required-scanners
33
+ Requires-Dist: bandit==1.9.4; extra == "required-scanners"
34
+ Requires-Dist: pip-audit==2.10.1; extra == "required-scanners"
35
+ Provides-Extra: python-scanners
36
+ Requires-Dist: secure-code-agent[required-scanners]; extra == "python-scanners"
37
+ Requires-Dist: semgrep<2,>=1.172; extra == "python-scanners"
38
+ Requires-Dist: checkov<4,>=3.3; extra == "python-scanners"
29
39
  Provides-Extra: dev
40
+ Requires-Dist: secure-code-agent[required-scanners]; extra == "dev"
30
41
  Requires-Dist: pytest>=8.0; extra == "dev"
31
42
  Requires-Dist: pytest-cov>=4.0; extra == "dev"
32
- Requires-Dist: ruff>=0.5; extra == "dev"
43
+ Requires-Dist: ruff==0.16.2; extra == "dev"
33
44
  Dynamic: license-file
34
45
 
35
46
  # secure-code-agent
@@ -38,7 +49,7 @@ Dynamic: license-file
38
49
  > Anchored to NIST SSDF · OWASP ASVS · OWASP Top 10 · MITRE CWE Top 25 · OpenSSF Scorecard · SARIF 2.1.0.
39
50
 
40
51
  ```bash
41
- pip install secure-code-agent
52
+ pip install 'secure-code-agent[required-scanners]'
42
53
 
43
54
  secure-code-agent --fail-on-gate \
44
55
  --output secure-code-report.md \
@@ -115,18 +126,21 @@ Hand the prompt to Claude Code, Codex, Cursor, Copilot, or any agent. The agent
115
126
 
116
127
  ## Standards anchored, not invented
117
128
 
118
- Every finding maps to five public standards. Operators see *which standard is failing*, not just *which scanner shouted*.
129
+ Known rules map to fields from five public standards. Unmapped and scanner-control
130
+ findings retain null standards fields rather than receiving invented mappings.
119
131
 
120
132
  | Source | What we use it for |
121
133
  |----------------------------------------------|----------------------------------------------------------|
122
134
  | [NIST SSDF SP 800-218](https://csrc.nist.gov/pubs/sp/800/218/final) | Process practice id (e.g. `PW.5.1`) |
123
135
  | [OWASP Top 10 (2021)](https://owasp.org/Top10/2021/) | Risk bucket (e.g. `A03:2021-Injection`) |
124
136
  | [OWASP ASVS 5.0](https://github.com/OWASP/ASVS) | Verification requirement (e.g. `V5.3`) |
125
- | [MITRE CWE Top 25 (2025)](https://cwe.mitre.org/top25/) | Canonical weakness id the dedupe key |
137
+ | [MITRE CWE Top 25 (2025)](https://cwe.mitre.org/top25/) | Canonical weakness id used in stable fingerprints |
126
138
  | [OpenSSF Scorecard](https://openssf.org/projects/scorecard/) | Repo + supply-chain hygiene |
127
139
  | [SARIF 2.1.0](https://www.oasis-open.org/standard/sarif-v2-1-0/) | Output format (and external scanner ingest) |
128
140
 
129
- When Semgrep, CodeQL, and Bandit fire on the same SQL-injection sink with three different rule ids, they all map to `CWE-89` and the scorer counts **one** underlying weakness. Not three.
141
+ When scanners map a finding to `CWE-89`, the canonical CWE participates in its
142
+ stable fingerprint and baseline identity. Cross-scanner findings are not yet
143
+ collapsed before scoring; reports preserve the original scanner evidence.
130
144
 
131
145
  ## Architecture (orchestrator, not engine)
132
146
 
@@ -203,7 +217,67 @@ Scoring math + worked examples in [`docs/scoring.md`](docs/scoring.md).
203
217
 
204
218
  Any tripped gate is a nonzero exit. Compose freely.
205
219
 
206
- ## Suppressions you can't game
220
+ `require_scanners` is a coverage gate, not a vulnerability gate. A required
221
+ scanner must resolve and complete successfully. Missing executables, timeouts,
222
+ invalid output, unsupported inputs, or excluding the scanner with CLI filters
223
+ fail coverage. Optional scanner failures produce `PARTIAL` coverage without
224
+ turning a clean finding set into a false comprehensive result. Markdown and
225
+ JSON reports record each scanner's outcome, resolved command, and version.
226
+ When `require_scanners` is present it must name at least one scanner; an empty
227
+ list is rejected instead of silently removing the structural coverage gate.
228
+
229
+ External scanners are not bundled, and the agent never installs one for you —
230
+ a gate that fetches and runs binaries to satisfy its own coverage requirement
231
+ is the supply-chain risk it is supposed to catch. Resolution order is an
232
+ explicit `scanners.<name>.command`, the active `PATH`, then `python -m <module>`
233
+ for supported Python scanners. Relative executable paths resolve from the scan
234
+ target and are executed with `shell=False`.
235
+
236
+ `secure-code-agent --preflight` reports which enabled scanners resolve on this
237
+ host, with versions and the install command for anything missing, and exits
238
+ nonzero when a required scanner is unavailable — so a missing toolchain costs a
239
+ second instead of a full audit. Bandit and pip-audit install as
240
+ `secure-code-agent[required-scanners]`; Semgrep and Checkov add
241
+ `[python-scanners]`. The remaining scanners are standalone binaries that cannot
242
+ come from PyPI: install them with your package manager, or run their pinned
243
+ upstream CI action and feed us the SARIF, which counts as coverage:
244
+
245
+ ```bash
246
+ secure-code-agent --fail-on-gate --sarif-import trivy.sarif
247
+ ```
248
+
249
+ An import satisfies `require_scanners` for the tool that produced it. An
250
+ unreadable, malformed, or run-less import fails the gate rather than ingesting
251
+ nothing quietly, an import reporting its own `executionSuccessful: false` is
252
+ recorded as failed, and when a scanner reports both locally and by import the
253
+ worse outcome wins. See [`docs/scanners.md`](docs/scanners.md) for the full
254
+ install matrix.
255
+
256
+ ```json
257
+ {
258
+ "scanners": {
259
+ "bandit": {
260
+ "enabled": true,
261
+ "command": [".audit-tools/bin/python", "-m", "bandit"]
262
+ },
263
+ "pip_audit": {
264
+ "enabled": true,
265
+ "command": [".audit-tools/bin/python", "-m", "pip_audit"],
266
+ "mode": "project",
267
+ "inputs": ["engine/pyproject.toml"]
268
+ }
269
+ }
270
+ }
271
+ ```
272
+
273
+ The tool never downloads a scanner during an audit. Install and pin scanner
274
+ versions in the audit environment or CI image.
275
+
276
+ This repository's own CI audits `requirements-audit.txt`, which pins the
277
+ minimum supported runtime dependency version. Project mode remains available
278
+ for repositories whose `pyproject.toml` is their authoritative audit input.
279
+
280
+ ## Time-bounded suppressions
207
281
 
208
282
  `.scignore.yaml` — every suppression requires a `reason` AND an `expires` date (max 365 days). Past-expiry suppressions become CRITICAL findings on their own. You can't ship `reason: "we'll fix it later"` forever.
209
283
 
@@ -228,15 +302,19 @@ Wildcard rule (`rule_id: "*"`) requires a `file` or `paths` scope — you cannot
228
302
  - Findings present in baseline → **acknowledged**; don't trip `fail_on_new`.
229
303
  - Findings missing from baseline → **new**; trip the gate.
230
304
 
231
- Bumping a CRITICAL or HIGH finding into the baseline requires `--bump-baseline --i-acknowledge-risk`. The bump records the operator's git `user.email` per fingerprint so PR review can see who acknowledged what.
305
+ `--bump-baseline` rewrites the baseline from the current findings. The file is
306
+ plain JSON and must be reviewed like any other security-policy change. This
307
+ release does not implement an interactive acknowledgment. Baseline entries
308
+ record the best-effort local Git email, while repository review policy remains
309
+ the approval boundary.
232
310
 
233
311
  This lets legacy repos adopt the gate without a 200-finding day-one cleanup.
234
312
 
235
313
  ## Quickstart
236
314
 
237
315
  ```bash
238
- # Install
239
- pip install secure-code-agent
316
+ # Install the orchestrator with its pinned default Bandit + pip-audit toolchain
317
+ pip install 'secure-code-agent[required-scanners]'
240
318
 
241
319
  # Initialize agent standards files for your AI coding tools
242
320
  secure-code-agent --init-agent-standards \
@@ -251,14 +329,18 @@ secure-code-agent --config secure-code-agent.json \
251
329
  --comment-output secure-code-pr-comment.md \
252
330
  --prompt-output secure-code-remediation-prompt.md
253
331
 
254
- # Audit only changed files since main
255
- secure-code-agent --changed-only main...HEAD --fail-on-new
256
-
257
332
  # Ingest external scanner SARIF (CodeQL, Snyk, Trivy, etc.)
258
333
  secure-code-agent --sarif-import codeql-results.sarif \
259
334
  --sarif-import snyk-results.sarif
260
335
  ```
261
336
 
337
+ `--changed-only` is reserved but not yet safely implemented. Passing it fails
338
+ with exit code 2 so a caller cannot accidentally treat an unscoped audit as a
339
+ changed-file audit.
340
+
341
+ The current orchestrator accepts one repository root per invocation. Multiple
342
+ positional roots fail with exit code 2 instead of silently ignoring coverage.
343
+
262
344
  ## Invokable skill / slash command
263
345
 
264
346
  For agents that support invokable skills, this repo ships a portable skill under [`skills/secure-code-agent/`](skills/secure-code-agent/). The `SKILL.md` body is the source of truth; per-host adapters live under `agents/` and `copilot/`.
@@ -272,14 +354,19 @@ For agents that support invokable skills, this repo ships a portable skill under
272
354
  ## GitHub Action
273
355
 
274
356
  ```yaml
275
- - uses: marshallguillory86/secure-code-agent@v0.1.0
357
+ - uses: marshallguillory86/secure-code-agent@v0.3.0
276
358
  with:
277
359
  config: secure-code-agent.json
278
- changed-only: main...HEAD
279
360
  fail-on-gate: true
280
361
  ```
281
362
 
282
- The action uploads SARIF to GitHub Code Scanning by default. See [`action.yml`](action.yml) and [`examples/github-actions/`](examples/github-actions/) for full workflows.
363
+ The action installs the exact source bundled with the referenced action plus
364
+ the pinned `required-scanners` extra (Bandit and pip-audit),
365
+ emits Markdown, JSON, SARIF, PR-comment, and remediation artifacts, and uploads
366
+ SARIF by default. The calling workflow must grant `security-events: write` for
367
+ SARIF upload. Pin production usage to a full commit SHA; the version tag above
368
+ is shown for readability. See [`action.yml`](action.yml) and
369
+ [`examples/github-actions/`](examples/github-actions/) for full workflows.
283
370
 
284
371
  ## What this is NOT
285
372
 
@@ -294,7 +381,7 @@ The action uploads SARIF to GitHub Code Scanning by default. See [`action.yml`](
294
381
  1. **Deterministic first, AI optional.** The audit never calls an LLM by default. The remediation prompt is a generated artifact you choose to hand to an agent.
295
382
  2. **Bounded scope.** The remediation prompt explicitly forbids touching crypto, auth, validation, logging, and tests.
296
383
  3. **Standards-anchored.** Five public standards (NIST / OWASP-x3 / CWE) — no invented taxonomy.
297
- 4. **CWE-deduped scoring.** One underlying weakness = one finding, regardless of how many scanners found it.
384
+ 4. **Stable finding identity.** CWE, normalized path, and normalized evidence form the baseline fingerprint. Cross-scanner score deduplication remains future work.
298
385
  5. **No vendor lock-in.** Markdown, JSON, SARIF, plain files. Pipe anywhere.
299
386
  6. **CI-first, local-first.** Same binary in pre-commit, local CI, GitHub Actions, GitLab, Buildkite.
300
387
 
@@ -302,7 +389,10 @@ Full design philosophy in [`docs/design.md`](docs/design.md).
302
389
 
303
390
  ## Documentation
304
391
 
392
+ - [`docs/product-intent.md`](docs/product-intent.md) — Why this exists, who it serves, what it refuses to become
305
393
  - [`docs/design.md`](docs/design.md) — Architecture + non-goals + scanner protocol
394
+ - [`docs/architecture.md`](docs/architecture.md) — Audit of the system as built + remediation sequence
395
+ - [`docs/release-blockers.md`](docs/release-blockers.md) — Open v0.3.0 release blockers (do not tag until closed)
306
396
  - [`docs/standards.md`](docs/standards.md) — NIST SSDF / OWASP / CWE / Scorecard / SARIF citations
307
397
  - [`docs/scoring.md`](docs/scoring.md) — Weighting model + worked examples
308
398
  - [`docs/scanners.md`](docs/scanners.md) — Per-scanner integrations + caveats
@@ -312,7 +402,8 @@ Full design philosophy in [`docs/design.md`](docs/design.md).
312
402
  ## Versioning
313
403
 
314
404
  - **Semver.** v0.x is pre-1.0 — the config schema may evolve. v1.0 locks it.
315
- - **SARIF 2.1.0** output is pinned and validated against the OASIS schema in CI.
405
+ - **SARIF 2.1.0-shaped** output is structurally unit-tested and round-tripped;
406
+ full OASIS schema validation is not yet part of CI.
316
407
 
317
408
  ## Get in touch
318
409
 
@@ -1,44 +1,10 @@
1
- Metadata-Version: 2.4
2
- Name: secure-code-agent
3
- Version: 0.2.0
4
- Summary: Deterministic security gate + bounded AI remediation prompt generator. NIST SSDF / OWASP ASVS / CWE Top 25 anchored.
5
- Author: Marshall Guillory
6
- License: MIT
7
- Project-URL: Homepage, https://github.com/marshallguillory86/secure-code-agent
8
- Project-URL: Repository, https://github.com/marshallguillory86/secure-code-agent
9
- Project-URL: Documentation, https://github.com/marshallguillory86/secure-code-agent/tree/main/docs
10
- Project-URL: Issues, https://github.com/marshallguillory86/secure-code-agent/issues
11
- Project-URL: Changelog, https://github.com/marshallguillory86/secure-code-agent/blob/main/CHANGELOG.md
12
- Keywords: security,appsec,sast,sca,secret-scanning,owasp,asvs,cwe,nist-ssdf,sarif,ai-code-review,ai-guardrail,remediation-prompt,ci,audit
13
- Classifier: Development Status :: 4 - Beta
14
- Classifier: Intended Audience :: Developers
15
- Classifier: Intended Audience :: Information Technology
16
- Classifier: License :: OSI Approved :: MIT License
17
- Classifier: Operating System :: POSIX
18
- Classifier: Operating System :: MacOS
19
- Classifier: Operating System :: POSIX :: Linux
20
- Classifier: Programming Language :: Python :: 3
21
- Classifier: Programming Language :: Python :: 3.10
22
- Classifier: Programming Language :: Python :: 3.11
23
- Classifier: Programming Language :: Python :: 3.12
24
- Classifier: Topic :: Security
25
- Classifier: Topic :: Software Development :: Quality Assurance
26
- Requires-Python: >=3.10
27
- Description-Content-Type: text/markdown
28
- License-File: LICENSE
29
- Provides-Extra: dev
30
- Requires-Dist: pytest>=8.0; extra == "dev"
31
- Requires-Dist: pytest-cov>=4.0; extra == "dev"
32
- Requires-Dist: ruff>=0.5; extra == "dev"
33
- Dynamic: license-file
34
-
35
1
  # secure-code-agent
36
2
 
37
3
  > **Deterministic security gate + bounded AI remediation prompts for repos with AI coding agents in the loop.**
38
4
  > Anchored to NIST SSDF · OWASP ASVS · OWASP Top 10 · MITRE CWE Top 25 · OpenSSF Scorecard · SARIF 2.1.0.
39
5
 
40
6
  ```bash
41
- pip install secure-code-agent
7
+ pip install 'secure-code-agent[required-scanners]'
42
8
 
43
9
  secure-code-agent --fail-on-gate \
44
10
  --output secure-code-report.md \
@@ -115,18 +81,21 @@ Hand the prompt to Claude Code, Codex, Cursor, Copilot, or any agent. The agent
115
81
 
116
82
  ## Standards anchored, not invented
117
83
 
118
- Every finding maps to five public standards. Operators see *which standard is failing*, not just *which scanner shouted*.
84
+ Known rules map to fields from five public standards. Unmapped and scanner-control
85
+ findings retain null standards fields rather than receiving invented mappings.
119
86
 
120
87
  | Source | What we use it for |
121
88
  |----------------------------------------------|----------------------------------------------------------|
122
89
  | [NIST SSDF SP 800-218](https://csrc.nist.gov/pubs/sp/800/218/final) | Process practice id (e.g. `PW.5.1`) |
123
90
  | [OWASP Top 10 (2021)](https://owasp.org/Top10/2021/) | Risk bucket (e.g. `A03:2021-Injection`) |
124
91
  | [OWASP ASVS 5.0](https://github.com/OWASP/ASVS) | Verification requirement (e.g. `V5.3`) |
125
- | [MITRE CWE Top 25 (2025)](https://cwe.mitre.org/top25/) | Canonical weakness id the dedupe key |
92
+ | [MITRE CWE Top 25 (2025)](https://cwe.mitre.org/top25/) | Canonical weakness id used in stable fingerprints |
126
93
  | [OpenSSF Scorecard](https://openssf.org/projects/scorecard/) | Repo + supply-chain hygiene |
127
94
  | [SARIF 2.1.0](https://www.oasis-open.org/standard/sarif-v2-1-0/) | Output format (and external scanner ingest) |
128
95
 
129
- When Semgrep, CodeQL, and Bandit fire on the same SQL-injection sink with three different rule ids, they all map to `CWE-89` and the scorer counts **one** underlying weakness. Not three.
96
+ When scanners map a finding to `CWE-89`, the canonical CWE participates in its
97
+ stable fingerprint and baseline identity. Cross-scanner findings are not yet
98
+ collapsed before scoring; reports preserve the original scanner evidence.
130
99
 
131
100
  ## Architecture (orchestrator, not engine)
132
101
 
@@ -203,7 +172,67 @@ Scoring math + worked examples in [`docs/scoring.md`](docs/scoring.md).
203
172
 
204
173
  Any tripped gate is a nonzero exit. Compose freely.
205
174
 
206
- ## Suppressions you can't game
175
+ `require_scanners` is a coverage gate, not a vulnerability gate. A required
176
+ scanner must resolve and complete successfully. Missing executables, timeouts,
177
+ invalid output, unsupported inputs, or excluding the scanner with CLI filters
178
+ fail coverage. Optional scanner failures produce `PARTIAL` coverage without
179
+ turning a clean finding set into a false comprehensive result. Markdown and
180
+ JSON reports record each scanner's outcome, resolved command, and version.
181
+ When `require_scanners` is present it must name at least one scanner; an empty
182
+ list is rejected instead of silently removing the structural coverage gate.
183
+
184
+ External scanners are not bundled, and the agent never installs one for you —
185
+ a gate that fetches and runs binaries to satisfy its own coverage requirement
186
+ is the supply-chain risk it is supposed to catch. Resolution order is an
187
+ explicit `scanners.<name>.command`, the active `PATH`, then `python -m <module>`
188
+ for supported Python scanners. Relative executable paths resolve from the scan
189
+ target and are executed with `shell=False`.
190
+
191
+ `secure-code-agent --preflight` reports which enabled scanners resolve on this
192
+ host, with versions and the install command for anything missing, and exits
193
+ nonzero when a required scanner is unavailable — so a missing toolchain costs a
194
+ second instead of a full audit. Bandit and pip-audit install as
195
+ `secure-code-agent[required-scanners]`; Semgrep and Checkov add
196
+ `[python-scanners]`. The remaining scanners are standalone binaries that cannot
197
+ come from PyPI: install them with your package manager, or run their pinned
198
+ upstream CI action and feed us the SARIF, which counts as coverage:
199
+
200
+ ```bash
201
+ secure-code-agent --fail-on-gate --sarif-import trivy.sarif
202
+ ```
203
+
204
+ An import satisfies `require_scanners` for the tool that produced it. An
205
+ unreadable, malformed, or run-less import fails the gate rather than ingesting
206
+ nothing quietly, an import reporting its own `executionSuccessful: false` is
207
+ recorded as failed, and when a scanner reports both locally and by import the
208
+ worse outcome wins. See [`docs/scanners.md`](docs/scanners.md) for the full
209
+ install matrix.
210
+
211
+ ```json
212
+ {
213
+ "scanners": {
214
+ "bandit": {
215
+ "enabled": true,
216
+ "command": [".audit-tools/bin/python", "-m", "bandit"]
217
+ },
218
+ "pip_audit": {
219
+ "enabled": true,
220
+ "command": [".audit-tools/bin/python", "-m", "pip_audit"],
221
+ "mode": "project",
222
+ "inputs": ["engine/pyproject.toml"]
223
+ }
224
+ }
225
+ }
226
+ ```
227
+
228
+ The tool never downloads a scanner during an audit. Install and pin scanner
229
+ versions in the audit environment or CI image.
230
+
231
+ This repository's own CI audits `requirements-audit.txt`, which pins the
232
+ minimum supported runtime dependency version. Project mode remains available
233
+ for repositories whose `pyproject.toml` is their authoritative audit input.
234
+
235
+ ## Time-bounded suppressions
207
236
 
208
237
  `.scignore.yaml` — every suppression requires a `reason` AND an `expires` date (max 365 days). Past-expiry suppressions become CRITICAL findings on their own. You can't ship `reason: "we'll fix it later"` forever.
209
238
 
@@ -228,15 +257,19 @@ Wildcard rule (`rule_id: "*"`) requires a `file` or `paths` scope — you cannot
228
257
  - Findings present in baseline → **acknowledged**; don't trip `fail_on_new`.
229
258
  - Findings missing from baseline → **new**; trip the gate.
230
259
 
231
- Bumping a CRITICAL or HIGH finding into the baseline requires `--bump-baseline --i-acknowledge-risk`. The bump records the operator's git `user.email` per fingerprint so PR review can see who acknowledged what.
260
+ `--bump-baseline` rewrites the baseline from the current findings. The file is
261
+ plain JSON and must be reviewed like any other security-policy change. This
262
+ release does not implement an interactive acknowledgment. Baseline entries
263
+ record the best-effort local Git email, while repository review policy remains
264
+ the approval boundary.
232
265
 
233
266
  This lets legacy repos adopt the gate without a 200-finding day-one cleanup.
234
267
 
235
268
  ## Quickstart
236
269
 
237
270
  ```bash
238
- # Install
239
- pip install secure-code-agent
271
+ # Install the orchestrator with its pinned default Bandit + pip-audit toolchain
272
+ pip install 'secure-code-agent[required-scanners]'
240
273
 
241
274
  # Initialize agent standards files for your AI coding tools
242
275
  secure-code-agent --init-agent-standards \
@@ -251,14 +284,18 @@ secure-code-agent --config secure-code-agent.json \
251
284
  --comment-output secure-code-pr-comment.md \
252
285
  --prompt-output secure-code-remediation-prompt.md
253
286
 
254
- # Audit only changed files since main
255
- secure-code-agent --changed-only main...HEAD --fail-on-new
256
-
257
287
  # Ingest external scanner SARIF (CodeQL, Snyk, Trivy, etc.)
258
288
  secure-code-agent --sarif-import codeql-results.sarif \
259
289
  --sarif-import snyk-results.sarif
260
290
  ```
261
291
 
292
+ `--changed-only` is reserved but not yet safely implemented. Passing it fails
293
+ with exit code 2 so a caller cannot accidentally treat an unscoped audit as a
294
+ changed-file audit.
295
+
296
+ The current orchestrator accepts one repository root per invocation. Multiple
297
+ positional roots fail with exit code 2 instead of silently ignoring coverage.
298
+
262
299
  ## Invokable skill / slash command
263
300
 
264
301
  For agents that support invokable skills, this repo ships a portable skill under [`skills/secure-code-agent/`](skills/secure-code-agent/). The `SKILL.md` body is the source of truth; per-host adapters live under `agents/` and `copilot/`.
@@ -272,14 +309,19 @@ For agents that support invokable skills, this repo ships a portable skill under
272
309
  ## GitHub Action
273
310
 
274
311
  ```yaml
275
- - uses: marshallguillory86/secure-code-agent@v0.1.0
312
+ - uses: marshallguillory86/secure-code-agent@v0.3.0
276
313
  with:
277
314
  config: secure-code-agent.json
278
- changed-only: main...HEAD
279
315
  fail-on-gate: true
280
316
  ```
281
317
 
282
- The action uploads SARIF to GitHub Code Scanning by default. See [`action.yml`](action.yml) and [`examples/github-actions/`](examples/github-actions/) for full workflows.
318
+ The action installs the exact source bundled with the referenced action plus
319
+ the pinned `required-scanners` extra (Bandit and pip-audit),
320
+ emits Markdown, JSON, SARIF, PR-comment, and remediation artifacts, and uploads
321
+ SARIF by default. The calling workflow must grant `security-events: write` for
322
+ SARIF upload. Pin production usage to a full commit SHA; the version tag above
323
+ is shown for readability. See [`action.yml`](action.yml) and
324
+ [`examples/github-actions/`](examples/github-actions/) for full workflows.
283
325
 
284
326
  ## What this is NOT
285
327
 
@@ -294,7 +336,7 @@ The action uploads SARIF to GitHub Code Scanning by default. See [`action.yml`](
294
336
  1. **Deterministic first, AI optional.** The audit never calls an LLM by default. The remediation prompt is a generated artifact you choose to hand to an agent.
295
337
  2. **Bounded scope.** The remediation prompt explicitly forbids touching crypto, auth, validation, logging, and tests.
296
338
  3. **Standards-anchored.** Five public standards (NIST / OWASP-x3 / CWE) — no invented taxonomy.
297
- 4. **CWE-deduped scoring.** One underlying weakness = one finding, regardless of how many scanners found it.
339
+ 4. **Stable finding identity.** CWE, normalized path, and normalized evidence form the baseline fingerprint. Cross-scanner score deduplication remains future work.
298
340
  5. **No vendor lock-in.** Markdown, JSON, SARIF, plain files. Pipe anywhere.
299
341
  6. **CI-first, local-first.** Same binary in pre-commit, local CI, GitHub Actions, GitLab, Buildkite.
300
342
 
@@ -302,7 +344,10 @@ Full design philosophy in [`docs/design.md`](docs/design.md).
302
344
 
303
345
  ## Documentation
304
346
 
347
+ - [`docs/product-intent.md`](docs/product-intent.md) — Why this exists, who it serves, what it refuses to become
305
348
  - [`docs/design.md`](docs/design.md) — Architecture + non-goals + scanner protocol
349
+ - [`docs/architecture.md`](docs/architecture.md) — Audit of the system as built + remediation sequence
350
+ - [`docs/release-blockers.md`](docs/release-blockers.md) — Open v0.3.0 release blockers (do not tag until closed)
306
351
  - [`docs/standards.md`](docs/standards.md) — NIST SSDF / OWASP / CWE / Scorecard / SARIF citations
307
352
  - [`docs/scoring.md`](docs/scoring.md) — Weighting model + worked examples
308
353
  - [`docs/scanners.md`](docs/scanners.md) — Per-scanner integrations + caveats
@@ -312,7 +357,8 @@ Full design philosophy in [`docs/design.md`](docs/design.md).
312
357
  ## Versioning
313
358
 
314
359
  - **Semver.** v0.x is pre-1.0 — the config schema may evolve. v1.0 locks it.
315
- - **SARIF 2.1.0** output is pinned and validated against the OASIS schema in CI.
360
+ - **SARIF 2.1.0-shaped** output is structurally unit-tested and round-tripped;
361
+ full OASIS schema validation is not yet part of CI.
316
362
 
317
363
  ## Get in touch
318
364
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "secure-code-agent"
7
- version = "0.2.0"
7
+ version = "0.3.0"
8
8
  description = "Deterministic security gate + bounded AI remediation prompt generator. NIST SSDF / OWASP ASVS / CWE Top 25 anchored."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -30,19 +30,43 @@ classifiers = [
30
30
  "Programming Language :: Python :: 3.10",
31
31
  "Programming Language :: Python :: 3.11",
32
32
  "Programming Language :: Python :: 3.12",
33
+ "Programming Language :: Python :: 3.13",
34
+ "Programming Language :: Python :: 3.14",
33
35
  "Topic :: Security",
34
36
  "Topic :: Software Development :: Quality Assurance",
35
37
  ]
36
38
  dependencies = [
37
- # Stdlib-only at runtime. Scanners are invoked as subprocesses so
38
- # the tool has no hard dependency on their Python packages.
39
+ "PyYAML>=6.0.2,<7",
40
+ # Scanner packages remain external subprocess dependencies.
39
41
  ]
40
42
 
41
43
  [project.optional-dependencies]
44
+ # Scanners the default configuration lists in gates.require_scanners. Pinned
45
+ # exactly: a gate asserting "bandit completed" should mean a known Bandit
46
+ # completed, not whatever Bandit resolved that day.
47
+ required-scanners = [
48
+ "bandit==1.9.4",
49
+ "pip-audit==2.10.1",
50
+ ]
51
+ # The remaining Python-packaged scanners. Bounded rather than pinned because
52
+ # they are optional; the coverage gate does not require them by default.
53
+ # Scanners distributed as standalone binaries (gitleaks, hadolint, npm,
54
+ # osv-scanner, scorecard, trivy, trufflehog) cannot be installed from PyPI —
55
+ # see docs/scanners.md for their install commands and the SARIF-import route.
56
+ python-scanners = [
57
+ "secure-code-agent[required-scanners]",
58
+ "semgrep>=1.172,<2",
59
+ "checkov>=3.3,<4",
60
+ ]
61
+ # Includes required-scanners so a fresh checkout can run its own audit.
62
+ # Ruff is pinned exactly: `ruff format --check` is a CI gate, and an
63
+ # unpinned formatter turns every upstream release into a possible red build
64
+ # with no change in this repository. Same reasoning as required-scanners.
42
65
  dev = [
66
+ "secure-code-agent[required-scanners]",
43
67
  "pytest>=8.0",
44
68
  "pytest-cov>=4.0",
45
- "ruff>=0.5",
69
+ "ruff==0.16.2",
46
70
  ]
47
71
 
48
72
  [project.scripts]
@@ -65,6 +89,10 @@ secure_code_audit = ["data/*.json", "data/*.yaml"]
65
89
  [tool.ruff]
66
90
  line-length = 100
67
91
  target-version = "py310"
92
+ # Ruff 0.16 formats Python blocks embedded in Markdown. Our docs use those
93
+ # blocks illustratively — excerpted statements, aligned-comment schemas — and
94
+ # reformatting them damages the documentation without improving any code.
95
+ extend-exclude = ["**/*.md"]
68
96
 
69
97
  [tool.ruff.lint]
70
98
  select = ["E", "F", "I", "B", "UP", "SIM", "C4"]