secure-code-agent 0.2.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 (38) hide show
  1. secure_code_agent-0.2.0/LICENSE +21 -0
  2. secure_code_agent-0.2.0/PKG-INFO +328 -0
  3. secure_code_agent-0.2.0/README.md +294 -0
  4. secure_code_agent-0.2.0/pyproject.toml +87 -0
  5. secure_code_agent-0.2.0/setup.cfg +4 -0
  6. secure_code_agent-0.2.0/src/secure_code_agent.egg-info/PKG-INFO +328 -0
  7. secure_code_agent-0.2.0/src/secure_code_agent.egg-info/SOURCES.txt +36 -0
  8. secure_code_agent-0.2.0/src/secure_code_agent.egg-info/dependency_links.txt +1 -0
  9. secure_code_agent-0.2.0/src/secure_code_agent.egg-info/entry_points.txt +3 -0
  10. secure_code_agent-0.2.0/src/secure_code_agent.egg-info/requires.txt +5 -0
  11. secure_code_agent-0.2.0/src/secure_code_agent.egg-info/top_level.txt +1 -0
  12. secure_code_agent-0.2.0/src/secure_code_audit/__init__.py +3 -0
  13. secure_code_agent-0.2.0/src/secure_code_audit/baseline.py +110 -0
  14. secure_code_agent-0.2.0/src/secure_code_audit/cli.py +258 -0
  15. secure_code_agent-0.2.0/src/secure_code_audit/config.py +115 -0
  16. secure_code_agent-0.2.0/src/secure_code_audit/findings.py +165 -0
  17. secure_code_agent-0.2.0/src/secure_code_audit/git_tools.py +82 -0
  18. secure_code_agent-0.2.0/src/secure_code_audit/instructions.py +141 -0
  19. secure_code_agent-0.2.0/src/secure_code_audit/remediation.py +168 -0
  20. secure_code_agent-0.2.0/src/secure_code_audit/renderers.py +253 -0
  21. secure_code_agent-0.2.0/src/secure_code_audit/sarif.py +221 -0
  22. secure_code_agent-0.2.0/src/secure_code_audit/scanners/__init__.py +50 -0
  23. secure_code_agent-0.2.0/src/secure_code_audit/scanners/bandit_scanner.py +83 -0
  24. secure_code_agent-0.2.0/src/secure_code_audit/scanners/base.py +194 -0
  25. secure_code_agent-0.2.0/src/secure_code_audit/scanners/builtin_rules.py +183 -0
  26. secure_code_agent-0.2.0/src/secure_code_audit/scanners/checkov_scanner.py +69 -0
  27. secure_code_agent-0.2.0/src/secure_code_audit/scanners/gitleaks_scanner.py +86 -0
  28. secure_code_agent-0.2.0/src/secure_code_audit/scanners/hadolint_scanner.py +107 -0
  29. secure_code_agent-0.2.0/src/secure_code_audit/scanners/npm_audit_scanner.py +101 -0
  30. secure_code_agent-0.2.0/src/secure_code_audit/scanners/osv_scanner.py +108 -0
  31. secure_code_agent-0.2.0/src/secure_code_audit/scanners/pip_audit_scanner.py +83 -0
  32. secure_code_agent-0.2.0/src/secure_code_audit/scanners/scorecard_scanner.py +156 -0
  33. secure_code_agent-0.2.0/src/secure_code_audit/scanners/semgrep_scanner.py +119 -0
  34. secure_code_agent-0.2.0/src/secure_code_audit/scanners/trivy_scanner.py +87 -0
  35. secure_code_agent-0.2.0/src/secure_code_audit/scanners/trufflehog_scanner.py +91 -0
  36. secure_code_agent-0.2.0/src/secure_code_audit/scoring.py +280 -0
  37. secure_code_agent-0.2.0/src/secure_code_audit/standards.py +391 -0
  38. secure_code_agent-0.2.0/src/secure_code_audit/suppressions.py +175 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Marshall Guillory
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,328 @@
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
+ # secure-code-agent
36
+
37
+ > **Deterministic security gate + bounded AI remediation prompts for repos with AI coding agents in the loop.**
38
+ > Anchored to NIST SSDF · OWASP ASVS · OWASP Top 10 · MITRE CWE Top 25 · OpenSSF Scorecard · SARIF 2.1.0.
39
+
40
+ ```bash
41
+ pip install secure-code-agent
42
+
43
+ secure-code-agent --fail-on-gate \
44
+ --output secure-code-report.md \
45
+ --prompt-output secure-code-remediation-prompt.md \
46
+ --sarif-output secure-code.sarif
47
+ ```
48
+
49
+ 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: security, not maintainability.
50
+
51
+ ---
52
+
53
+ ## Why this exists
54
+
55
+ AI coding agents ship code at human-review-saturating speed. Point them at a security finding and the documented anti-patterns are:
56
+
57
+ | Anti-pattern | What the agent actually does |
58
+ |----------------------------------------------------|----------------------------------------------------------------------------------------------------|
59
+ | **Crypto roulette** | "Replace MD5 with SHA-256" → rewrites the hashing module to use a library it saw in training data.|
60
+ | **Auth-flow rewrite** | "Fix the IDOR" → refactors the session model. Now you have an unaudited new auth path. |
61
+ | **Validation softening** | "Make the tests pass after the fix" → weakens the regex / removes the bounds check. |
62
+ | **Test deletion** | "The security test is failing" → deletes the test. |
63
+ | **Lint disable** | "This rule fires repeatedly" → `# nosec`, `# noqa`, `eslint-disable` everywhere. |
64
+ | **Scope creep** | "I fixed the SQLi" → followed by 600 lines of unrelated refactoring. |
65
+ | **Dependency thrash** | "Bumping the vulnerable package" → introduces 12 unrelated new dependencies. |
66
+ | **Silent behavior change** | "It works now" → same input, different output. Downstream callers break. |
67
+
68
+ Existing scanners (Semgrep, Bandit, CodeQL, Snyk, Trivy) emit findings. None of them ship a **bounded prompt back to the agent** that says *"fix only these specific findings, do not touch crypto/auth/validation/logging, preserve behavior."*
69
+
70
+ That gap is what this tool fills.
71
+
72
+ ## The output that matters
73
+
74
+ Every other security scanner stops at "here's a list of findings." `secure-code-agent` generates a remediation prompt:
75
+
76
+ ```markdown
77
+ # Security remediation — bounded scope
78
+
79
+ You are fixing the security findings listed in §FINDINGS below.
80
+ This is a constrained task, not a refactor.
81
+
82
+ ## Hard constraints (MUST NOT violate)
83
+
84
+ 1. Fix only the findings listed in §FINDINGS. Do not touch unrelated
85
+ code, files, or modules.
86
+ 2. Do not change cryptographic algorithms, key derivation, IV/nonce
87
+ handling, padding modes, or random sources unless a finding in
88
+ §FINDINGS explicitly names them as the defect.
89
+ 3. Do not change authentication flows, session handling, token
90
+ lifetime, cookie attributes, or authorization gates unless a
91
+ finding in §FINDINGS explicitly names them.
92
+ 4. Do not weaken input validation, output encoding, sanitization,
93
+ bounds checks, regex strictness, or rate limits to make existing
94
+ tests pass.
95
+ 5. Do not disable, delete, or skip security tests. Do not remove
96
+ `@_limiter.limit`, `@require_auth`, `@require_csrf`, or similar
97
+ decorators.
98
+ 6. Do not silence linter warnings via `# nosec`, `# noqa`, `# type:
99
+ ignore`, `eslint-disable`, `sonar-disable`, or equivalent.
100
+ 7. Do not introduce new third-party dependencies. Prefer stdlib or
101
+ already-vendored libraries.
102
+ 8. Preserve behavior. Same inputs must produce the same outputs
103
+ unless a finding explicitly proves the current behavior is unsafe.
104
+ 9. Add a focused test that exercises the specific security boundary
105
+ you fixed. The test must FAIL on the pre-fix code and PASS on
106
+ the post-fix code. No "TODO: add test later".
107
+ 10. Keep the patch small. If you find yourself rewriting a function
108
+ rather than patching it, stop and report the structural issue.
109
+
110
+ ## §FINDINGS
111
+ ...
112
+ ```
113
+
114
+ Hand the prompt to Claude Code, Codex, Cursor, Copilot, or any agent. The agent now has explicit boundaries. The full template + rationale lives in [`docs/remediation.md`](docs/remediation.md).
115
+
116
+ ## Standards anchored, not invented
117
+
118
+ Every finding maps to five public standards. Operators see *which standard is failing*, not just *which scanner shouted*.
119
+
120
+ | Source | What we use it for |
121
+ |----------------------------------------------|----------------------------------------------------------|
122
+ | [NIST SSDF SP 800-218](https://csrc.nist.gov/pubs/sp/800/218/final) | Process practice id (e.g. `PW.5.1`) |
123
+ | [OWASP Top 10 (2021)](https://owasp.org/Top10/2021/) | Risk bucket (e.g. `A03:2021-Injection`) |
124
+ | [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 |
126
+ | [OpenSSF Scorecard](https://openssf.org/projects/scorecard/) | Repo + supply-chain hygiene |
127
+ | [SARIF 2.1.0](https://www.oasis-open.org/standard/sarif-v2-1-0/) | Output format (and external scanner ingest) |
128
+
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.
130
+
131
+ ## Architecture (orchestrator, not engine)
132
+
133
+ ```text
134
+ ┌────────────────────────────────────────────────────────────────────┐
135
+ │ secure-code-agent CLI │
136
+ │ │
137
+ │ Config → Scanners (subprocess) → Findings → Scoring → Renderers │
138
+ │ │
139
+ │ ┌──────────────────┐ │
140
+ │ │ Markdown report │ │
141
+ │ │ JSON │ │
142
+ │ │ SARIF 2.1.0 │ │
143
+ │ │ PR comment │ │
144
+ │ │ Remediation 🪄 │ │
145
+ │ │ Agent standards │ │
146
+ │ └──────────────────┘ │
147
+ └────────────────────────────────────────────────────────────────────┘
148
+
149
+ │ Scanners (subprocess, version-isolated):
150
+
151
+ ├── Bandit (Python SAST)
152
+ ├── Semgrep (multi-language SAST + SARIF ingest)
153
+ ├── pip-audit (Python SCA)
154
+ ├── npm audit (Node SCA)
155
+ ├── Gitleaks (secret scanning, history-aware)
156
+ ├── TruffleHog (verified secret scanning)
157
+ ├── Trivy (containers / IaC / k8s / vuln / secret)
158
+ ├── Checkov (Terraform / CloudFormation / Helm / k8s)
159
+ ├── Hadolint (Dockerfile lint)
160
+ ├── OSV-Scanner (multi-ecosystem SCA via osv.dev)
161
+ ├── OpenSSF Scorecard (repo hygiene + supply chain)
162
+ ├── eslint-plugin-security (JS/TS SAST) [v0.3]
163
+ ├── CodeQL SARIF (ingest GitHub-hosted analysis)
164
+ └── Built-in regex rules (high-confidence, low-FP)
165
+ ```
166
+
167
+ We don't reimplement SAST. We invoke best-in-class scanners as subprocesses, parse their canonical output, normalize across CWE/OWASP/ASVS/SSDF, and produce one ranked view.
168
+
169
+ Full architecture in [`docs/design.md`](docs/design.md).
170
+
171
+ ## Audit categories (9 buckets, 1 grade)
172
+
173
+ Findings roll up to nine canonical categories. The grade is driven by the **worst category** — one CRITICAL secret in git history shouldn't be offset by a clean dependency tree.
174
+
175
+ | Category | Examples |
176
+ |-------------------------|---------------------------------------------------------------------------------|
177
+ | `secrets` | Hardcoded API keys, tokens in history, `.env` committed |
178
+ | `dependencies` | CVE in pinned dep, yanked package, abandoned upstream |
179
+ | `code_vulnerabilities` | SQLi, XSS, command-injection, path-traversal, SSRF, XXE, deserialization |
180
+ | `auth_authz` | Missing auth gate, IDOR, broken access control, JWT misuse |
181
+ | `crypto` | Weak alg, hardcoded IV, ECB, MD5/SHA-1 for security, missing constant-time |
182
+ | `supply_chain` | Unpinned action, missing SBOM, no signed releases, low Scorecard |
183
+ | `config_iac` | World-readable S3, public security group, Dockerfile `USER root`, k8s privileged|
184
+ | `logging_observability` | Secrets in logs, PII in URLs, missing audit trail on auth events |
185
+ | `policy_docs` | Missing SECURITY.md, no responsible-disclosure path, no threat model |
186
+
187
+ Scoring math + worked examples in [`docs/scoring.md`](docs/scoring.md).
188
+
189
+ ## Hard gates
190
+
191
+ ```json
192
+ {
193
+ "gates": {
194
+ "fail_on_severity": ["critical", "high"],
195
+ "fail_on_category": ["secrets", "auth_authz"],
196
+ "fail_on_new": true,
197
+ "min_score": 4.0,
198
+ "require_scanners": ["bandit", "gitleaks"],
199
+ "max_unsuppressed": { "critical": 0, "high": 0, "medium": 10 }
200
+ }
201
+ }
202
+ ```
203
+
204
+ Any tripped gate is a nonzero exit. Compose freely.
205
+
206
+ ## Suppressions you can't game
207
+
208
+ `.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
+
210
+ ```yaml
211
+ - file: services/legacy_billing.py
212
+ rule_id: "*"
213
+ reason: "Slated for rewrite Q3 2026 — gated by initiative INV-44."
214
+ expires: "2026-09-30"
215
+
216
+ - rule_id: "B101"
217
+ paths: ["tests/"]
218
+ reason: "assert statements are legitimate in test code."
219
+ expires: "2027-05-13"
220
+ ```
221
+
222
+ Wildcard rule (`rule_id: "*"`) requires a `file` or `paths` scope — you cannot disable a rule globally.
223
+
224
+ ## Baseline + incremental adoption
225
+
226
+ `secure-code-baseline.json` fingerprints every current finding. On the next run:
227
+
228
+ - Findings present in baseline → **acknowledged**; don't trip `fail_on_new`.
229
+ - Findings missing from baseline → **new**; trip the gate.
230
+
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.
232
+
233
+ This lets legacy repos adopt the gate without a 200-finding day-one cleanup.
234
+
235
+ ## Quickstart
236
+
237
+ ```bash
238
+ # Install
239
+ pip install secure-code-agent
240
+
241
+ # Initialize agent standards files for your AI coding tools
242
+ secure-code-agent --init-agent-standards \
243
+ --target codex --target claude-code --target cursor --target copilot
244
+
245
+ # Run an audit with hard-gate exit
246
+ secure-code-agent --config secure-code-agent.json \
247
+ --fail-on-gate \
248
+ --output secure-code-report.md \
249
+ --json-output secure-code-report.json \
250
+ --sarif-output secure-code.sarif \
251
+ --comment-output secure-code-pr-comment.md \
252
+ --prompt-output secure-code-remediation-prompt.md
253
+
254
+ # Audit only changed files since main
255
+ secure-code-agent --changed-only main...HEAD --fail-on-new
256
+
257
+ # Ingest external scanner SARIF (CodeQL, Snyk, Trivy, etc.)
258
+ secure-code-agent --sarif-import codeql-results.sarif \
259
+ --sarif-import snyk-results.sarif
260
+ ```
261
+
262
+ ## Invokable skill / slash command
263
+
264
+ 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/`.
265
+
266
+ | Host | Install destination | Invocation |
267
+ |---------------------------|-------------------------------------------------------------------------------------|-------------------------------------|
268
+ | Codex / OpenAI | wired via `skills/secure-code-agent/agents/openai.yaml` | per Codex's skills convention |
269
+ | Claude Code | `cp -r skills/secure-code-agent ~/.claude/skills/` | `/secure-code-agent` |
270
+ | GitHub Copilot (VS Code) | `cp skills/secure-code-agent/copilot/secure-code-agent.prompt.md .github/prompts/` | `/secure-code-agent` in Copilot Chat |
271
+
272
+ ## GitHub Action
273
+
274
+ ```yaml
275
+ - uses: marshallguillory86/secure-code-agent@v0.1.0
276
+ with:
277
+ config: secure-code-agent.json
278
+ changed-only: main...HEAD
279
+ fail-on-gate: true
280
+ ```
281
+
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.
283
+
284
+ ## What this is NOT
285
+
286
+ - ❌ **Not a SAST engine.** We delegate to Semgrep / Bandit / CodeQL / etc. — we don't write yet another AST analyzer.
287
+ - ❌ **Not a runtime defense.** No WAF, no IDS, no agent in the request path. Static + supply-chain + config only.
288
+ - ❌ **Not a SaaS.** Findings live as files in your repo. No telemetry. No version-check ping.
289
+ - ❌ **Not a license scanner.** Pair with `pip-licenses` / `license-checker` separately.
290
+ - ❌ **Not an exploit generator.** No DAST, no fuzzing.
291
+
292
+ ## Design principles
293
+
294
+ 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
+ 2. **Bounded scope.** The remediation prompt explicitly forbids touching crypto, auth, validation, logging, and tests.
296
+ 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.
298
+ 5. **No vendor lock-in.** Markdown, JSON, SARIF, plain files. Pipe anywhere.
299
+ 6. **CI-first, local-first.** Same binary in pre-commit, local CI, GitHub Actions, GitLab, Buildkite.
300
+
301
+ Full design philosophy in [`docs/design.md`](docs/design.md).
302
+
303
+ ## Documentation
304
+
305
+ - [`docs/design.md`](docs/design.md) — Architecture + non-goals + scanner protocol
306
+ - [`docs/standards.md`](docs/standards.md) — NIST SSDF / OWASP / CWE / Scorecard / SARIF citations
307
+ - [`docs/scoring.md`](docs/scoring.md) — Weighting model + worked examples
308
+ - [`docs/scanners.md`](docs/scanners.md) — Per-scanner integrations + caveats
309
+ - [`docs/remediation.md`](docs/remediation.md) — The prompt template + failure-mode rationale
310
+ - [`docs/threat-model.md`](docs/threat-model.md) — What we defend against (and what we don't)
311
+
312
+ ## Versioning
313
+
314
+ - **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.
316
+
317
+ ## Get in touch
318
+
319
+ - Bug reports / feature requests — [GitHub Issues](https://github.com/marshallguillory86/secure-code-agent/issues)
320
+ - Security vulnerabilities in this tool — see [`SECURITY.md`](SECURITY.md)
321
+
322
+ ## License
323
+
324
+ MIT — see [`LICENSE`](LICENSE).
325
+
326
+ ---
327
+
328
+ Built by [Marshall Guillory](https://github.com/marshallguillory86). The companion to [`maintainability-agent`](https://github.com/marshallguillory86/maintainability-agent) — both tools encode a single thesis: *AI agents need deterministic boundaries, not best-effort guardrails.*
@@ -0,0 +1,294 @@
1
+ # secure-code-agent
2
+
3
+ > **Deterministic security gate + bounded AI remediation prompts for repos with AI coding agents in the loop.**
4
+ > Anchored to NIST SSDF · OWASP ASVS · OWASP Top 10 · MITRE CWE Top 25 · OpenSSF Scorecard · SARIF 2.1.0.
5
+
6
+ ```bash
7
+ pip install secure-code-agent
8
+
9
+ secure-code-agent --fail-on-gate \
10
+ --output secure-code-report.md \
11
+ --prompt-output secure-code-remediation-prompt.md \
12
+ --sarif-output secure-code.sarif
13
+ ```
14
+
15
+ 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: security, not maintainability.
16
+
17
+ ---
18
+
19
+ ## Why this exists
20
+
21
+ AI coding agents ship code at human-review-saturating speed. Point them at a security finding and the documented anti-patterns are:
22
+
23
+ | Anti-pattern | What the agent actually does |
24
+ |----------------------------------------------------|----------------------------------------------------------------------------------------------------|
25
+ | **Crypto roulette** | "Replace MD5 with SHA-256" → rewrites the hashing module to use a library it saw in training data.|
26
+ | **Auth-flow rewrite** | "Fix the IDOR" → refactors the session model. Now you have an unaudited new auth path. |
27
+ | **Validation softening** | "Make the tests pass after the fix" → weakens the regex / removes the bounds check. |
28
+ | **Test deletion** | "The security test is failing" → deletes the test. |
29
+ | **Lint disable** | "This rule fires repeatedly" → `# nosec`, `# noqa`, `eslint-disable` everywhere. |
30
+ | **Scope creep** | "I fixed the SQLi" → followed by 600 lines of unrelated refactoring. |
31
+ | **Dependency thrash** | "Bumping the vulnerable package" → introduces 12 unrelated new dependencies. |
32
+ | **Silent behavior change** | "It works now" → same input, different output. Downstream callers break. |
33
+
34
+ Existing scanners (Semgrep, Bandit, CodeQL, Snyk, Trivy) emit findings. None of them ship a **bounded prompt back to the agent** that says *"fix only these specific findings, do not touch crypto/auth/validation/logging, preserve behavior."*
35
+
36
+ That gap is what this tool fills.
37
+
38
+ ## The output that matters
39
+
40
+ Every other security scanner stops at "here's a list of findings." `secure-code-agent` generates a remediation prompt:
41
+
42
+ ```markdown
43
+ # Security remediation — bounded scope
44
+
45
+ You are fixing the security findings listed in §FINDINGS below.
46
+ This is a constrained task, not a refactor.
47
+
48
+ ## Hard constraints (MUST NOT violate)
49
+
50
+ 1. Fix only the findings listed in §FINDINGS. Do not touch unrelated
51
+ code, files, or modules.
52
+ 2. Do not change cryptographic algorithms, key derivation, IV/nonce
53
+ handling, padding modes, or random sources unless a finding in
54
+ §FINDINGS explicitly names them as the defect.
55
+ 3. Do not change authentication flows, session handling, token
56
+ lifetime, cookie attributes, or authorization gates unless a
57
+ finding in §FINDINGS explicitly names them.
58
+ 4. Do not weaken input validation, output encoding, sanitization,
59
+ bounds checks, regex strictness, or rate limits to make existing
60
+ tests pass.
61
+ 5. Do not disable, delete, or skip security tests. Do not remove
62
+ `@_limiter.limit`, `@require_auth`, `@require_csrf`, or similar
63
+ decorators.
64
+ 6. Do not silence linter warnings via `# nosec`, `# noqa`, `# type:
65
+ ignore`, `eslint-disable`, `sonar-disable`, or equivalent.
66
+ 7. Do not introduce new third-party dependencies. Prefer stdlib or
67
+ already-vendored libraries.
68
+ 8. Preserve behavior. Same inputs must produce the same outputs
69
+ unless a finding explicitly proves the current behavior is unsafe.
70
+ 9. Add a focused test that exercises the specific security boundary
71
+ you fixed. The test must FAIL on the pre-fix code and PASS on
72
+ the post-fix code. No "TODO: add test later".
73
+ 10. Keep the patch small. If you find yourself rewriting a function
74
+ rather than patching it, stop and report the structural issue.
75
+
76
+ ## §FINDINGS
77
+ ...
78
+ ```
79
+
80
+ Hand the prompt to Claude Code, Codex, Cursor, Copilot, or any agent. The agent now has explicit boundaries. The full template + rationale lives in [`docs/remediation.md`](docs/remediation.md).
81
+
82
+ ## Standards anchored, not invented
83
+
84
+ Every finding maps to five public standards. Operators see *which standard is failing*, not just *which scanner shouted*.
85
+
86
+ | Source | What we use it for |
87
+ |----------------------------------------------|----------------------------------------------------------|
88
+ | [NIST SSDF SP 800-218](https://csrc.nist.gov/pubs/sp/800/218/final) | Process practice id (e.g. `PW.5.1`) |
89
+ | [OWASP Top 10 (2021)](https://owasp.org/Top10/2021/) | Risk bucket (e.g. `A03:2021-Injection`) |
90
+ | [OWASP ASVS 5.0](https://github.com/OWASP/ASVS) | Verification requirement (e.g. `V5.3`) |
91
+ | [MITRE CWE Top 25 (2025)](https://cwe.mitre.org/top25/) | Canonical weakness id — the dedupe key |
92
+ | [OpenSSF Scorecard](https://openssf.org/projects/scorecard/) | Repo + supply-chain hygiene |
93
+ | [SARIF 2.1.0](https://www.oasis-open.org/standard/sarif-v2-1-0/) | Output format (and external scanner ingest) |
94
+
95
+ 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
+
97
+ ## Architecture (orchestrator, not engine)
98
+
99
+ ```text
100
+ ┌────────────────────────────────────────────────────────────────────┐
101
+ │ secure-code-agent CLI │
102
+ │ │
103
+ │ Config → Scanners (subprocess) → Findings → Scoring → Renderers │
104
+ │ │
105
+ │ ┌──────────────────┐ │
106
+ │ │ Markdown report │ │
107
+ │ │ JSON │ │
108
+ │ │ SARIF 2.1.0 │ │
109
+ │ │ PR comment │ │
110
+ │ │ Remediation 🪄 │ │
111
+ │ │ Agent standards │ │
112
+ │ └──────────────────┘ │
113
+ └────────────────────────────────────────────────────────────────────┘
114
+
115
+ │ Scanners (subprocess, version-isolated):
116
+
117
+ ├── Bandit (Python SAST)
118
+ ├── Semgrep (multi-language SAST + SARIF ingest)
119
+ ├── pip-audit (Python SCA)
120
+ ├── npm audit (Node SCA)
121
+ ├── Gitleaks (secret scanning, history-aware)
122
+ ├── TruffleHog (verified secret scanning)
123
+ ├── Trivy (containers / IaC / k8s / vuln / secret)
124
+ ├── Checkov (Terraform / CloudFormation / Helm / k8s)
125
+ ├── Hadolint (Dockerfile lint)
126
+ ├── OSV-Scanner (multi-ecosystem SCA via osv.dev)
127
+ ├── OpenSSF Scorecard (repo hygiene + supply chain)
128
+ ├── eslint-plugin-security (JS/TS SAST) [v0.3]
129
+ ├── CodeQL SARIF (ingest GitHub-hosted analysis)
130
+ └── Built-in regex rules (high-confidence, low-FP)
131
+ ```
132
+
133
+ We don't reimplement SAST. We invoke best-in-class scanners as subprocesses, parse their canonical output, normalize across CWE/OWASP/ASVS/SSDF, and produce one ranked view.
134
+
135
+ Full architecture in [`docs/design.md`](docs/design.md).
136
+
137
+ ## Audit categories (9 buckets, 1 grade)
138
+
139
+ Findings roll up to nine canonical categories. The grade is driven by the **worst category** — one CRITICAL secret in git history shouldn't be offset by a clean dependency tree.
140
+
141
+ | Category | Examples |
142
+ |-------------------------|---------------------------------------------------------------------------------|
143
+ | `secrets` | Hardcoded API keys, tokens in history, `.env` committed |
144
+ | `dependencies` | CVE in pinned dep, yanked package, abandoned upstream |
145
+ | `code_vulnerabilities` | SQLi, XSS, command-injection, path-traversal, SSRF, XXE, deserialization |
146
+ | `auth_authz` | Missing auth gate, IDOR, broken access control, JWT misuse |
147
+ | `crypto` | Weak alg, hardcoded IV, ECB, MD5/SHA-1 for security, missing constant-time |
148
+ | `supply_chain` | Unpinned action, missing SBOM, no signed releases, low Scorecard |
149
+ | `config_iac` | World-readable S3, public security group, Dockerfile `USER root`, k8s privileged|
150
+ | `logging_observability` | Secrets in logs, PII in URLs, missing audit trail on auth events |
151
+ | `policy_docs` | Missing SECURITY.md, no responsible-disclosure path, no threat model |
152
+
153
+ Scoring math + worked examples in [`docs/scoring.md`](docs/scoring.md).
154
+
155
+ ## Hard gates
156
+
157
+ ```json
158
+ {
159
+ "gates": {
160
+ "fail_on_severity": ["critical", "high"],
161
+ "fail_on_category": ["secrets", "auth_authz"],
162
+ "fail_on_new": true,
163
+ "min_score": 4.0,
164
+ "require_scanners": ["bandit", "gitleaks"],
165
+ "max_unsuppressed": { "critical": 0, "high": 0, "medium": 10 }
166
+ }
167
+ }
168
+ ```
169
+
170
+ Any tripped gate is a nonzero exit. Compose freely.
171
+
172
+ ## Suppressions you can't game
173
+
174
+ `.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.
175
+
176
+ ```yaml
177
+ - file: services/legacy_billing.py
178
+ rule_id: "*"
179
+ reason: "Slated for rewrite Q3 2026 — gated by initiative INV-44."
180
+ expires: "2026-09-30"
181
+
182
+ - rule_id: "B101"
183
+ paths: ["tests/"]
184
+ reason: "assert statements are legitimate in test code."
185
+ expires: "2027-05-13"
186
+ ```
187
+
188
+ Wildcard rule (`rule_id: "*"`) requires a `file` or `paths` scope — you cannot disable a rule globally.
189
+
190
+ ## Baseline + incremental adoption
191
+
192
+ `secure-code-baseline.json` fingerprints every current finding. On the next run:
193
+
194
+ - Findings present in baseline → **acknowledged**; don't trip `fail_on_new`.
195
+ - Findings missing from baseline → **new**; trip the gate.
196
+
197
+ 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.
198
+
199
+ This lets legacy repos adopt the gate without a 200-finding day-one cleanup.
200
+
201
+ ## Quickstart
202
+
203
+ ```bash
204
+ # Install
205
+ pip install secure-code-agent
206
+
207
+ # Initialize agent standards files for your AI coding tools
208
+ secure-code-agent --init-agent-standards \
209
+ --target codex --target claude-code --target cursor --target copilot
210
+
211
+ # Run an audit with hard-gate exit
212
+ secure-code-agent --config secure-code-agent.json \
213
+ --fail-on-gate \
214
+ --output secure-code-report.md \
215
+ --json-output secure-code-report.json \
216
+ --sarif-output secure-code.sarif \
217
+ --comment-output secure-code-pr-comment.md \
218
+ --prompt-output secure-code-remediation-prompt.md
219
+
220
+ # Audit only changed files since main
221
+ secure-code-agent --changed-only main...HEAD --fail-on-new
222
+
223
+ # Ingest external scanner SARIF (CodeQL, Snyk, Trivy, etc.)
224
+ secure-code-agent --sarif-import codeql-results.sarif \
225
+ --sarif-import snyk-results.sarif
226
+ ```
227
+
228
+ ## Invokable skill / slash command
229
+
230
+ 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/`.
231
+
232
+ | Host | Install destination | Invocation |
233
+ |---------------------------|-------------------------------------------------------------------------------------|-------------------------------------|
234
+ | Codex / OpenAI | wired via `skills/secure-code-agent/agents/openai.yaml` | per Codex's skills convention |
235
+ | Claude Code | `cp -r skills/secure-code-agent ~/.claude/skills/` | `/secure-code-agent` |
236
+ | GitHub Copilot (VS Code) | `cp skills/secure-code-agent/copilot/secure-code-agent.prompt.md .github/prompts/` | `/secure-code-agent` in Copilot Chat |
237
+
238
+ ## GitHub Action
239
+
240
+ ```yaml
241
+ - uses: marshallguillory86/secure-code-agent@v0.1.0
242
+ with:
243
+ config: secure-code-agent.json
244
+ changed-only: main...HEAD
245
+ fail-on-gate: true
246
+ ```
247
+
248
+ 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.
249
+
250
+ ## What this is NOT
251
+
252
+ - ❌ **Not a SAST engine.** We delegate to Semgrep / Bandit / CodeQL / etc. — we don't write yet another AST analyzer.
253
+ - ❌ **Not a runtime defense.** No WAF, no IDS, no agent in the request path. Static + supply-chain + config only.
254
+ - ❌ **Not a SaaS.** Findings live as files in your repo. No telemetry. No version-check ping.
255
+ - ❌ **Not a license scanner.** Pair with `pip-licenses` / `license-checker` separately.
256
+ - ❌ **Not an exploit generator.** No DAST, no fuzzing.
257
+
258
+ ## Design principles
259
+
260
+ 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.
261
+ 2. **Bounded scope.** The remediation prompt explicitly forbids touching crypto, auth, validation, logging, and tests.
262
+ 3. **Standards-anchored.** Five public standards (NIST / OWASP-x3 / CWE) — no invented taxonomy.
263
+ 4. **CWE-deduped scoring.** One underlying weakness = one finding, regardless of how many scanners found it.
264
+ 5. **No vendor lock-in.** Markdown, JSON, SARIF, plain files. Pipe anywhere.
265
+ 6. **CI-first, local-first.** Same binary in pre-commit, local CI, GitHub Actions, GitLab, Buildkite.
266
+
267
+ Full design philosophy in [`docs/design.md`](docs/design.md).
268
+
269
+ ## Documentation
270
+
271
+ - [`docs/design.md`](docs/design.md) — Architecture + non-goals + scanner protocol
272
+ - [`docs/standards.md`](docs/standards.md) — NIST SSDF / OWASP / CWE / Scorecard / SARIF citations
273
+ - [`docs/scoring.md`](docs/scoring.md) — Weighting model + worked examples
274
+ - [`docs/scanners.md`](docs/scanners.md) — Per-scanner integrations + caveats
275
+ - [`docs/remediation.md`](docs/remediation.md) — The prompt template + failure-mode rationale
276
+ - [`docs/threat-model.md`](docs/threat-model.md) — What we defend against (and what we don't)
277
+
278
+ ## Versioning
279
+
280
+ - **Semver.** v0.x is pre-1.0 — the config schema may evolve. v1.0 locks it.
281
+ - **SARIF 2.1.0** output is pinned and validated against the OASIS schema in CI.
282
+
283
+ ## Get in touch
284
+
285
+ - Bug reports / feature requests — [GitHub Issues](https://github.com/marshallguillory86/secure-code-agent/issues)
286
+ - Security vulnerabilities in this tool — see [`SECURITY.md`](SECURITY.md)
287
+
288
+ ## License
289
+
290
+ MIT — see [`LICENSE`](LICENSE).
291
+
292
+ ---
293
+
294
+ Built by [Marshall Guillory](https://github.com/marshallguillory86). The companion to [`maintainability-agent`](https://github.com/marshallguillory86/maintainability-agent) — both tools encode a single thesis: *AI agents need deterministic boundaries, not best-effort guardrails.*