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.
- {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/PKG-INFO +109 -18
- secure_code_agent-0.2.0/src/secure_code_agent.egg-info/PKG-INFO → secure_code_agent-0.3.0/README.md +96 -50
- {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/pyproject.toml +32 -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
- {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_agent.egg-info/SOURCES.txt +1 -0
- secure_code_agent-0.3.0/src/secure_code_agent.egg-info/requires.txt +16 -0
- {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/__init__.py +1 -1
- {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/baseline.py +37 -28
- secure_code_agent-0.3.0/src/secure_code_audit/cli.py +515 -0
- secure_code_agent-0.3.0/src/secure_code_audit/config.py +264 -0
- {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/findings.py +66 -66
- {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/git_tools.py +3 -20
- {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/instructions.py +17 -18
- {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/remediation.py +2 -2
- secure_code_agent-0.3.0/src/secure_code_audit/renderers.py +364 -0
- secure_code_agent-0.3.0/src/secure_code_audit/sarif.py +363 -0
- secure_code_agent-0.3.0/src/secure_code_audit/scanner_status.py +169 -0
- secure_code_agent-0.3.0/src/secure_code_audit/scanners/__init__.py +47 -0
- secure_code_agent-0.3.0/src/secure_code_audit/scanners/bandit_scanner.py +121 -0
- {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/scanners/base.py +85 -32
- {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/scanners/builtin_rules.py +22 -18
- secure_code_agent-0.3.0/src/secure_code_audit/scanners/checkov_scanner.py +105 -0
- secure_code_agent-0.3.0/src/secure_code_audit/scanners/gitleaks_scanner.py +112 -0
- secure_code_agent-0.3.0/src/secure_code_audit/scanners/hadolint_scanner.py +159 -0
- secure_code_agent-0.3.0/src/secure_code_audit/scanners/npm_audit_scanner.py +158 -0
- secure_code_agent-0.3.0/src/secure_code_audit/scanners/osv_scanner.py +159 -0
- secure_code_agent-0.3.0/src/secure_code_audit/scanners/pip_audit_scanner.py +233 -0
- {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/scanners/scorecard_scanner.py +101 -42
- secure_code_agent-0.3.0/src/secure_code_audit/scanners/semgrep_scanner.py +156 -0
- {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/scanners/trivy_scanner.py +56 -17
- {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/scanners/trufflehog_scanner.py +55 -14
- {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/scoring.py +74 -59
- {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/standards.py +279 -147
- {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_audit/suppressions.py +53 -50
- secure_code_agent-0.2.0/src/secure_code_agent.egg-info/requires.txt +0 -5
- secure_code_agent-0.2.0/src/secure_code_audit/cli.py +0 -258
- secure_code_agent-0.2.0/src/secure_code_audit/config.py +0 -115
- secure_code_agent-0.2.0/src/secure_code_audit/renderers.py +0 -253
- secure_code_agent-0.2.0/src/secure_code_audit/sarif.py +0 -221
- secure_code_agent-0.2.0/src/secure_code_audit/scanners/__init__.py +0 -50
- secure_code_agent-0.2.0/src/secure_code_audit/scanners/bandit_scanner.py +0 -83
- secure_code_agent-0.2.0/src/secure_code_audit/scanners/checkov_scanner.py +0 -69
- secure_code_agent-0.2.0/src/secure_code_audit/scanners/gitleaks_scanner.py +0 -86
- secure_code_agent-0.2.0/src/secure_code_audit/scanners/hadolint_scanner.py +0 -107
- secure_code_agent-0.2.0/src/secure_code_audit/scanners/npm_audit_scanner.py +0 -101
- secure_code_agent-0.2.0/src/secure_code_audit/scanners/osv_scanner.py +0 -108
- secure_code_agent-0.2.0/src/secure_code_audit/scanners/pip_audit_scanner.py +0 -83
- secure_code_agent-0.2.0/src/secure_code_audit/scanners/semgrep_scanner.py +0 -119
- {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/LICENSE +0 -0
- {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/setup.cfg +0 -0
- {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_agent.egg-info/dependency_links.txt +0 -0
- {secure_code_agent-0.2.0 → secure_code_agent-0.3.0}/src/secure_code_agent.egg-info/entry_points.txt +0 -0
- {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.
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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. **
|
|
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
|
|
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
|
|
secure_code_agent-0.2.0/src/secure_code_agent.egg-info/PKG-INFO → secure_code_agent-0.3.0/README.md
RENAMED
|
@@ -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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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. **
|
|
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
|
|
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.
|
|
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
|
-
|
|
38
|
-
#
|
|
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
|
|
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"]
|