secure-code-agent 0.2.0__tar.gz → 0.4.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.4.0}/PKG-INFO +118 -19
- secure_code_agent-0.2.0/src/secure_code_agent.egg-info/PKG-INFO → secure_code_agent-0.4.0/README.md +104 -51
- {secure_code_agent-0.2.0 → secure_code_agent-0.4.0}/pyproject.toml +43 -4
- secure_code_agent-0.2.0/README.md → secure_code_agent-0.4.0/src/secure_code_agent.egg-info/PKG-INFO +150 -17
- {secure_code_agent-0.2.0 → secure_code_agent-0.4.0}/src/secure_code_agent.egg-info/SOURCES.txt +7 -0
- secure_code_agent-0.4.0/src/secure_code_agent.egg-info/requires.txt +17 -0
- {secure_code_agent-0.2.0 → secure_code_agent-0.4.0}/src/secure_code_audit/__init__.py +1 -1
- {secure_code_agent-0.2.0 → secure_code_agent-0.4.0}/src/secure_code_audit/baseline.py +37 -28
- secure_code_agent-0.4.0/src/secure_code_audit/cli.py +632 -0
- secure_code_agent-0.4.0/src/secure_code_audit/config.py +348 -0
- secure_code_agent-0.4.0/src/secure_code_audit/data/semgrep-offline.yaml +396 -0
- {secure_code_agent-0.2.0 → secure_code_agent-0.4.0}/src/secure_code_audit/findings.py +66 -66
- {secure_code_agent-0.2.0 → secure_code_agent-0.4.0}/src/secure_code_audit/git_tools.py +3 -20
- {secure_code_agent-0.2.0 → secure_code_agent-0.4.0}/src/secure_code_audit/instructions.py +17 -18
- {secure_code_agent-0.2.0 → secure_code_agent-0.4.0}/src/secure_code_audit/remediation.py +2 -2
- secure_code_agent-0.4.0/src/secure_code_audit/renderers.py +403 -0
- secure_code_agent-0.4.0/src/secure_code_audit/ruleset.py +97 -0
- secure_code_agent-0.4.0/src/secure_code_audit/sarif.py +408 -0
- secure_code_agent-0.4.0/src/secure_code_audit/scanner_status.py +197 -0
- secure_code_agent-0.4.0/src/secure_code_audit/scanners/__init__.py +53 -0
- secure_code_agent-0.4.0/src/secure_code_audit/scanners/bandit_scanner.py +98 -0
- secure_code_agent-0.4.0/src/secure_code_audit/scanners/base.py +376 -0
- {secure_code_agent-0.2.0 → secure_code_agent-0.4.0}/src/secure_code_audit/scanners/builtin_rules.py +25 -20
- {secure_code_agent-0.2.0 → secure_code_agent-0.4.0}/src/secure_code_audit/scanners/checkov_scanner.py +29 -23
- secure_code_agent-0.4.0/src/secure_code_audit/scanners/floor.py +282 -0
- secure_code_agent-0.4.0/src/secure_code_audit/scanners/gitleaks_scanner.py +103 -0
- secure_code_agent-0.4.0/src/secure_code_audit/scanners/gosec_scanner.py +132 -0
- {secure_code_agent-0.2.0 → secure_code_agent-0.4.0}/src/secure_code_audit/scanners/hadolint_scanner.py +29 -32
- secure_code_agent-0.4.0/src/secure_code_audit/scanners/njsscan_scanner.py +108 -0
- secure_code_agent-0.4.0/src/secure_code_audit/scanners/npm_audit_scanner.py +130 -0
- {secure_code_agent-0.2.0 → secure_code_agent-0.4.0}/src/secure_code_audit/scanners/osv_scanner.py +44 -36
- secure_code_agent-0.4.0/src/secure_code_audit/scanners/pip_audit_scanner.py +216 -0
- secure_code_agent-0.4.0/src/secure_code_audit/scanners/rubocop_scanner.py +118 -0
- {secure_code_agent-0.2.0 → secure_code_agent-0.4.0}/src/secure_code_audit/scanners/scorecard_scanner.py +58 -46
- secure_code_agent-0.4.0/src/secure_code_audit/scanners/semgrep_scanner.py +192 -0
- {secure_code_agent-0.2.0 → secure_code_agent-0.4.0}/src/secure_code_audit/scanners/trivy_scanner.py +28 -21
- {secure_code_agent-0.2.0 → secure_code_agent-0.4.0}/src/secure_code_audit/scanners/trufflehog_scanner.py +43 -18
- secure_code_agent-0.4.0/src/secure_code_audit/scoring.py +421 -0
- {secure_code_agent-0.2.0 → secure_code_agent-0.4.0}/src/secure_code_audit/standards.py +279 -147
- secure_code_agent-0.4.0/src/secure_code_audit/suppressions.py +246 -0
- 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/base.py +0 -194
- 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/npm_audit_scanner.py +0 -101
- 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/src/secure_code_audit/scoring.py +0 -280
- secure_code_agent-0.2.0/src/secure_code_audit/suppressions.py +0 -175
- {secure_code_agent-0.2.0 → secure_code_agent-0.4.0}/LICENSE +0 -0
- {secure_code_agent-0.2.0 → secure_code_agent-0.4.0}/setup.cfg +0 -0
- {secure_code_agent-0.2.0 → secure_code_agent-0.4.0}/src/secure_code_agent.egg-info/dependency_links.txt +0 -0
- {secure_code_agent-0.2.0 → secure_code_agent-0.4.0}/src/secure_code_agent.egg-info/entry_points.txt +0 -0
- {secure_code_agent-0.2.0 → secure_code_agent-0.4.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.4.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,27 @@ 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
|
+
Requires-Dist: njsscan==1.0.0; extra == "required-scanners"
|
|
36
|
+
Provides-Extra: python-scanners
|
|
37
|
+
Requires-Dist: secure-code-agent[required-scanners]; extra == "python-scanners"
|
|
38
|
+
Requires-Dist: semgrep<2,>=1.172; extra == "python-scanners"
|
|
39
|
+
Requires-Dist: checkov<4,>=3.3; extra == "python-scanners"
|
|
29
40
|
Provides-Extra: dev
|
|
41
|
+
Requires-Dist: secure-code-agent[required-scanners]; extra == "dev"
|
|
30
42
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
31
43
|
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
32
|
-
Requires-Dist: ruff
|
|
44
|
+
Requires-Dist: ruff==0.16.2; extra == "dev"
|
|
33
45
|
Dynamic: license-file
|
|
34
46
|
|
|
35
47
|
# secure-code-agent
|
|
@@ -38,7 +50,7 @@ Dynamic: license-file
|
|
|
38
50
|
> Anchored to NIST SSDF · OWASP ASVS · OWASP Top 10 · MITRE CWE Top 25 · OpenSSF Scorecard · SARIF 2.1.0.
|
|
39
51
|
|
|
40
52
|
```bash
|
|
41
|
-
pip install secure-code-agent
|
|
53
|
+
pip install 'secure-code-agent[required-scanners]'
|
|
42
54
|
|
|
43
55
|
secure-code-agent --fail-on-gate \
|
|
44
56
|
--output secure-code-report.md \
|
|
@@ -115,18 +127,21 @@ Hand the prompt to Claude Code, Codex, Cursor, Copilot, or any agent. The agent
|
|
|
115
127
|
|
|
116
128
|
## Standards anchored, not invented
|
|
117
129
|
|
|
118
|
-
|
|
130
|
+
Known rules map to fields from five public standards. Unmapped and scanner-control
|
|
131
|
+
findings retain null standards fields rather than receiving invented mappings.
|
|
119
132
|
|
|
120
133
|
| Source | What we use it for |
|
|
121
134
|
|----------------------------------------------|----------------------------------------------------------|
|
|
122
135
|
| [NIST SSDF SP 800-218](https://csrc.nist.gov/pubs/sp/800/218/final) | Process practice id (e.g. `PW.5.1`) |
|
|
123
136
|
| [OWASP Top 10 (2021)](https://owasp.org/Top10/2021/) | Risk bucket (e.g. `A03:2021-Injection`) |
|
|
124
137
|
| [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
|
|
138
|
+
| [MITRE CWE Top 25 (2025)](https://cwe.mitre.org/top25/) | Canonical weakness id used in stable fingerprints |
|
|
126
139
|
| [OpenSSF Scorecard](https://openssf.org/projects/scorecard/) | Repo + supply-chain hygiene |
|
|
127
140
|
| [SARIF 2.1.0](https://www.oasis-open.org/standard/sarif-v2-1-0/) | Output format (and external scanner ingest) |
|
|
128
141
|
|
|
129
|
-
When
|
|
142
|
+
When scanners map a finding to `CWE-89`, the canonical CWE participates in its
|
|
143
|
+
stable fingerprint and baseline identity. Cross-scanner findings are not yet
|
|
144
|
+
collapsed before scoring; reports preserve the original scanner evidence.
|
|
130
145
|
|
|
131
146
|
## Architecture (orchestrator, not engine)
|
|
132
147
|
|
|
@@ -159,7 +174,9 @@ When Semgrep, CodeQL, and Bandit fire on the same SQL-injection sink with three
|
|
|
159
174
|
├── Hadolint (Dockerfile lint)
|
|
160
175
|
├── OSV-Scanner (multi-ecosystem SCA via osv.dev)
|
|
161
176
|
├── OpenSSF Scorecard (repo hygiene + supply chain)
|
|
162
|
-
├──
|
|
177
|
+
├── njsscan (JS/TS SAST, offline, no Node needed)
|
|
178
|
+
├── RuboCop (Ruby security cops, --only Security)
|
|
179
|
+
├── gosec (Go SAST — opt-in; needs the Go toolchain)
|
|
163
180
|
├── CodeQL SARIF (ingest GitHub-hosted analysis)
|
|
164
181
|
└── Built-in regex rules (high-confidence, low-FP)
|
|
165
182
|
```
|
|
@@ -203,7 +220,67 @@ Scoring math + worked examples in [`docs/scoring.md`](docs/scoring.md).
|
|
|
203
220
|
|
|
204
221
|
Any tripped gate is a nonzero exit. Compose freely.
|
|
205
222
|
|
|
206
|
-
|
|
223
|
+
`require_scanners` is a coverage gate, not a vulnerability gate. A required
|
|
224
|
+
scanner must resolve and complete successfully. Missing executables, timeouts,
|
|
225
|
+
invalid output, unsupported inputs, or excluding the scanner with CLI filters
|
|
226
|
+
fail coverage. Optional scanner failures produce `PARTIAL` coverage without
|
|
227
|
+
turning a clean finding set into a false comprehensive result. Markdown and
|
|
228
|
+
JSON reports record each scanner's outcome, resolved command, and version.
|
|
229
|
+
When `require_scanners` is present it must name at least one scanner; an empty
|
|
230
|
+
list is rejected instead of silently removing the structural coverage gate.
|
|
231
|
+
|
|
232
|
+
External scanners are not bundled, and the agent never installs one for you —
|
|
233
|
+
a gate that fetches and runs binaries to satisfy its own coverage requirement
|
|
234
|
+
is the supply-chain risk it is supposed to catch. Resolution order is an
|
|
235
|
+
explicit `scanners.<name>.command`, the active `PATH`, then `python -m <module>`
|
|
236
|
+
for supported Python scanners. Relative executable paths resolve from the scan
|
|
237
|
+
target and are executed with `shell=False`.
|
|
238
|
+
|
|
239
|
+
`secure-code-agent --preflight` reports which enabled scanners resolve on this
|
|
240
|
+
host, with versions and the install command for anything missing, and exits
|
|
241
|
+
nonzero when a required scanner is unavailable — so a missing toolchain costs a
|
|
242
|
+
second instead of a full audit. Bandit and pip-audit install as
|
|
243
|
+
`secure-code-agent[required-scanners]`; Semgrep and Checkov add
|
|
244
|
+
`[python-scanners]`. The remaining scanners are standalone binaries that cannot
|
|
245
|
+
come from PyPI: install them with your package manager, or run their pinned
|
|
246
|
+
upstream CI action and feed us the SARIF, which counts as coverage:
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
secure-code-agent --fail-on-gate --sarif-import trivy.sarif
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
An import satisfies `require_scanners` for the tool that produced it. An
|
|
253
|
+
unreadable, malformed, or run-less import fails the gate rather than ingesting
|
|
254
|
+
nothing quietly, an import reporting its own `executionSuccessful: false` is
|
|
255
|
+
recorded as failed, and when a scanner reports both locally and by import the
|
|
256
|
+
worse outcome wins. See [`docs/scanners.md`](docs/scanners.md) for the full
|
|
257
|
+
install matrix.
|
|
258
|
+
|
|
259
|
+
```json
|
|
260
|
+
{
|
|
261
|
+
"scanners": {
|
|
262
|
+
"bandit": {
|
|
263
|
+
"enabled": true,
|
|
264
|
+
"command": [".audit-tools/bin/python", "-m", "bandit"]
|
|
265
|
+
},
|
|
266
|
+
"pip_audit": {
|
|
267
|
+
"enabled": true,
|
|
268
|
+
"command": [".audit-tools/bin/python", "-m", "pip_audit"],
|
|
269
|
+
"mode": "project",
|
|
270
|
+
"inputs": ["engine/pyproject.toml"]
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
The tool never downloads a scanner during an audit. Install and pin scanner
|
|
277
|
+
versions in the audit environment or CI image.
|
|
278
|
+
|
|
279
|
+
This repository's own CI audits `requirements-audit.txt`, which pins the
|
|
280
|
+
minimum supported runtime dependency version. Project mode remains available
|
|
281
|
+
for repositories whose `pyproject.toml` is their authoritative audit input.
|
|
282
|
+
|
|
283
|
+
## Time-bounded suppressions
|
|
207
284
|
|
|
208
285
|
`.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
286
|
|
|
@@ -213,6 +290,10 @@ Any tripped gate is a nonzero exit. Compose freely.
|
|
|
213
290
|
reason: "Slated for rewrite Q3 2026 — gated by initiative INV-44."
|
|
214
291
|
expires: "2026-09-30"
|
|
215
292
|
|
|
293
|
+
fingerprint: 0aaa689f8a967d8c # optional: pin to ONE finding (16 hex, from the report)
|
|
294
|
+
|
|
295
|
+
line: 18 # optional: with fingerprint, pins the exact location
|
|
296
|
+
|
|
216
297
|
- rule_id: "B101"
|
|
217
298
|
paths: ["tests/"]
|
|
218
299
|
reason: "assert statements are legitimate in test code."
|
|
@@ -228,15 +309,19 @@ Wildcard rule (`rule_id: "*"`) requires a `file` or `paths` scope — you cannot
|
|
|
228
309
|
- Findings present in baseline → **acknowledged**; don't trip `fail_on_new`.
|
|
229
310
|
- Findings missing from baseline → **new**; trip the gate.
|
|
230
311
|
|
|
231
|
-
|
|
312
|
+
`--bump-baseline` rewrites the baseline from the current findings. The file is
|
|
313
|
+
plain JSON and must be reviewed like any other security-policy change. This
|
|
314
|
+
release does not implement an interactive acknowledgment. Baseline entries
|
|
315
|
+
record the best-effort local Git email, while repository review policy remains
|
|
316
|
+
the approval boundary.
|
|
232
317
|
|
|
233
318
|
This lets legacy repos adopt the gate without a 200-finding day-one cleanup.
|
|
234
319
|
|
|
235
320
|
## Quickstart
|
|
236
321
|
|
|
237
322
|
```bash
|
|
238
|
-
# Install
|
|
239
|
-
pip install secure-code-agent
|
|
323
|
+
# Install the orchestrator with its pinned default Bandit + pip-audit toolchain
|
|
324
|
+
pip install 'secure-code-agent[required-scanners]'
|
|
240
325
|
|
|
241
326
|
# Initialize agent standards files for your AI coding tools
|
|
242
327
|
secure-code-agent --init-agent-standards \
|
|
@@ -251,14 +336,18 @@ secure-code-agent --config secure-code-agent.json \
|
|
|
251
336
|
--comment-output secure-code-pr-comment.md \
|
|
252
337
|
--prompt-output secure-code-remediation-prompt.md
|
|
253
338
|
|
|
254
|
-
# Audit only changed files since main
|
|
255
|
-
secure-code-agent --changed-only main...HEAD --fail-on-new
|
|
256
|
-
|
|
257
339
|
# Ingest external scanner SARIF (CodeQL, Snyk, Trivy, etc.)
|
|
258
340
|
secure-code-agent --sarif-import codeql-results.sarif \
|
|
259
341
|
--sarif-import snyk-results.sarif
|
|
260
342
|
```
|
|
261
343
|
|
|
344
|
+
`--changed-only` is reserved but not yet safely implemented. Passing it fails
|
|
345
|
+
with exit code 2 so a caller cannot accidentally treat an unscoped audit as a
|
|
346
|
+
changed-file audit.
|
|
347
|
+
|
|
348
|
+
The current orchestrator accepts one repository root per invocation. Multiple
|
|
349
|
+
positional roots fail with exit code 2 instead of silently ignoring coverage.
|
|
350
|
+
|
|
262
351
|
## Invokable skill / slash command
|
|
263
352
|
|
|
264
353
|
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 +361,19 @@ For agents that support invokable skills, this repo ships a portable skill under
|
|
|
272
361
|
## GitHub Action
|
|
273
362
|
|
|
274
363
|
```yaml
|
|
275
|
-
- uses: marshallguillory86/secure-code-agent@v0.
|
|
364
|
+
- uses: marshallguillory86/secure-code-agent@v0.3.0
|
|
276
365
|
with:
|
|
277
366
|
config: secure-code-agent.json
|
|
278
|
-
changed-only: main...HEAD
|
|
279
367
|
fail-on-gate: true
|
|
280
368
|
```
|
|
281
369
|
|
|
282
|
-
The action
|
|
370
|
+
The action installs the exact source bundled with the referenced action plus
|
|
371
|
+
the pinned `required-scanners` extra (Bandit and pip-audit),
|
|
372
|
+
emits Markdown, JSON, SARIF, PR-comment, and remediation artifacts, and uploads
|
|
373
|
+
SARIF by default. The calling workflow must grant `security-events: write` for
|
|
374
|
+
SARIF upload. Pin production usage to a full commit SHA; the version tag above
|
|
375
|
+
is shown for readability. See [`action.yml`](action.yml) and
|
|
376
|
+
[`examples/github-actions/`](examples/github-actions/) for full workflows.
|
|
283
377
|
|
|
284
378
|
## What this is NOT
|
|
285
379
|
|
|
@@ -294,7 +388,7 @@ The action uploads SARIF to GitHub Code Scanning by default. See [`action.yml`](
|
|
|
294
388
|
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
389
|
2. **Bounded scope.** The remediation prompt explicitly forbids touching crypto, auth, validation, logging, and tests.
|
|
296
390
|
3. **Standards-anchored.** Five public standards (NIST / OWASP-x3 / CWE) — no invented taxonomy.
|
|
297
|
-
4. **
|
|
391
|
+
4. **Stable finding identity.** CWE, normalized path, and normalized evidence form the baseline fingerprint. Cross-scanner score deduplication remains future work.
|
|
298
392
|
5. **No vendor lock-in.** Markdown, JSON, SARIF, plain files. Pipe anywhere.
|
|
299
393
|
6. **CI-first, local-first.** Same binary in pre-commit, local CI, GitHub Actions, GitLab, Buildkite.
|
|
300
394
|
|
|
@@ -302,7 +396,11 @@ Full design philosophy in [`docs/design.md`](docs/design.md).
|
|
|
302
396
|
|
|
303
397
|
## Documentation
|
|
304
398
|
|
|
399
|
+
- [`docs/product-intent.md`](docs/product-intent.md) — Why this exists, who it serves, what it refuses to become
|
|
400
|
+
- [`docs/decisions.md`](docs/decisions.md) — Decision register: rulings the code alone cannot answer
|
|
305
401
|
- [`docs/design.md`](docs/design.md) — Architecture + non-goals + scanner protocol
|
|
402
|
+
- [`docs/architecture.md`](docs/architecture.md) — Audit of the system as built + remediation sequence
|
|
403
|
+
- [`docs/release-blockers.md`](docs/release-blockers.md) — Open v0.3.0 release blockers (do not tag until closed)
|
|
306
404
|
- [`docs/standards.md`](docs/standards.md) — NIST SSDF / OWASP / CWE / Scorecard / SARIF citations
|
|
307
405
|
- [`docs/scoring.md`](docs/scoring.md) — Weighting model + worked examples
|
|
308
406
|
- [`docs/scanners.md`](docs/scanners.md) — Per-scanner integrations + caveats
|
|
@@ -312,7 +410,8 @@ Full design philosophy in [`docs/design.md`](docs/design.md).
|
|
|
312
410
|
## Versioning
|
|
313
411
|
|
|
314
412
|
- **Semver.** v0.x is pre-1.0 — the config schema may evolve. v1.0 locks it.
|
|
315
|
-
- **SARIF 2.1.0** output is
|
|
413
|
+
- **SARIF 2.1.0-shaped** output is structurally unit-tested and round-tripped;
|
|
414
|
+
full OASIS schema validation is not yet part of CI.
|
|
316
415
|
|
|
317
416
|
## Get in touch
|
|
318
417
|
|
secure_code_agent-0.2.0/src/secure_code_agent.egg-info/PKG-INFO → secure_code_agent-0.4.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
|
|
|
@@ -159,7 +128,9 @@ When Semgrep, CodeQL, and Bandit fire on the same SQL-injection sink with three
|
|
|
159
128
|
├── Hadolint (Dockerfile lint)
|
|
160
129
|
├── OSV-Scanner (multi-ecosystem SCA via osv.dev)
|
|
161
130
|
├── OpenSSF Scorecard (repo hygiene + supply chain)
|
|
162
|
-
├──
|
|
131
|
+
├── njsscan (JS/TS SAST, offline, no Node needed)
|
|
132
|
+
├── RuboCop (Ruby security cops, --only Security)
|
|
133
|
+
├── gosec (Go SAST — opt-in; needs the Go toolchain)
|
|
163
134
|
├── CodeQL SARIF (ingest GitHub-hosted analysis)
|
|
164
135
|
└── Built-in regex rules (high-confidence, low-FP)
|
|
165
136
|
```
|
|
@@ -203,7 +174,67 @@ Scoring math + worked examples in [`docs/scoring.md`](docs/scoring.md).
|
|
|
203
174
|
|
|
204
175
|
Any tripped gate is a nonzero exit. Compose freely.
|
|
205
176
|
|
|
206
|
-
|
|
177
|
+
`require_scanners` is a coverage gate, not a vulnerability gate. A required
|
|
178
|
+
scanner must resolve and complete successfully. Missing executables, timeouts,
|
|
179
|
+
invalid output, unsupported inputs, or excluding the scanner with CLI filters
|
|
180
|
+
fail coverage. Optional scanner failures produce `PARTIAL` coverage without
|
|
181
|
+
turning a clean finding set into a false comprehensive result. Markdown and
|
|
182
|
+
JSON reports record each scanner's outcome, resolved command, and version.
|
|
183
|
+
When `require_scanners` is present it must name at least one scanner; an empty
|
|
184
|
+
list is rejected instead of silently removing the structural coverage gate.
|
|
185
|
+
|
|
186
|
+
External scanners are not bundled, and the agent never installs one for you —
|
|
187
|
+
a gate that fetches and runs binaries to satisfy its own coverage requirement
|
|
188
|
+
is the supply-chain risk it is supposed to catch. Resolution order is an
|
|
189
|
+
explicit `scanners.<name>.command`, the active `PATH`, then `python -m <module>`
|
|
190
|
+
for supported Python scanners. Relative executable paths resolve from the scan
|
|
191
|
+
target and are executed with `shell=False`.
|
|
192
|
+
|
|
193
|
+
`secure-code-agent --preflight` reports which enabled scanners resolve on this
|
|
194
|
+
host, with versions and the install command for anything missing, and exits
|
|
195
|
+
nonzero when a required scanner is unavailable — so a missing toolchain costs a
|
|
196
|
+
second instead of a full audit. Bandit and pip-audit install as
|
|
197
|
+
`secure-code-agent[required-scanners]`; Semgrep and Checkov add
|
|
198
|
+
`[python-scanners]`. The remaining scanners are standalone binaries that cannot
|
|
199
|
+
come from PyPI: install them with your package manager, or run their pinned
|
|
200
|
+
upstream CI action and feed us the SARIF, which counts as coverage:
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
secure-code-agent --fail-on-gate --sarif-import trivy.sarif
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
An import satisfies `require_scanners` for the tool that produced it. An
|
|
207
|
+
unreadable, malformed, or run-less import fails the gate rather than ingesting
|
|
208
|
+
nothing quietly, an import reporting its own `executionSuccessful: false` is
|
|
209
|
+
recorded as failed, and when a scanner reports both locally and by import the
|
|
210
|
+
worse outcome wins. See [`docs/scanners.md`](docs/scanners.md) for the full
|
|
211
|
+
install matrix.
|
|
212
|
+
|
|
213
|
+
```json
|
|
214
|
+
{
|
|
215
|
+
"scanners": {
|
|
216
|
+
"bandit": {
|
|
217
|
+
"enabled": true,
|
|
218
|
+
"command": [".audit-tools/bin/python", "-m", "bandit"]
|
|
219
|
+
},
|
|
220
|
+
"pip_audit": {
|
|
221
|
+
"enabled": true,
|
|
222
|
+
"command": [".audit-tools/bin/python", "-m", "pip_audit"],
|
|
223
|
+
"mode": "project",
|
|
224
|
+
"inputs": ["engine/pyproject.toml"]
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
The tool never downloads a scanner during an audit. Install and pin scanner
|
|
231
|
+
versions in the audit environment or CI image.
|
|
232
|
+
|
|
233
|
+
This repository's own CI audits `requirements-audit.txt`, which pins the
|
|
234
|
+
minimum supported runtime dependency version. Project mode remains available
|
|
235
|
+
for repositories whose `pyproject.toml` is their authoritative audit input.
|
|
236
|
+
|
|
237
|
+
## Time-bounded suppressions
|
|
207
238
|
|
|
208
239
|
`.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
240
|
|
|
@@ -213,6 +244,10 @@ Any tripped gate is a nonzero exit. Compose freely.
|
|
|
213
244
|
reason: "Slated for rewrite Q3 2026 — gated by initiative INV-44."
|
|
214
245
|
expires: "2026-09-30"
|
|
215
246
|
|
|
247
|
+
fingerprint: 0aaa689f8a967d8c # optional: pin to ONE finding (16 hex, from the report)
|
|
248
|
+
|
|
249
|
+
line: 18 # optional: with fingerprint, pins the exact location
|
|
250
|
+
|
|
216
251
|
- rule_id: "B101"
|
|
217
252
|
paths: ["tests/"]
|
|
218
253
|
reason: "assert statements are legitimate in test code."
|
|
@@ -228,15 +263,19 @@ Wildcard rule (`rule_id: "*"`) requires a `file` or `paths` scope — you cannot
|
|
|
228
263
|
- Findings present in baseline → **acknowledged**; don't trip `fail_on_new`.
|
|
229
264
|
- Findings missing from baseline → **new**; trip the gate.
|
|
230
265
|
|
|
231
|
-
|
|
266
|
+
`--bump-baseline` rewrites the baseline from the current findings. The file is
|
|
267
|
+
plain JSON and must be reviewed like any other security-policy change. This
|
|
268
|
+
release does not implement an interactive acknowledgment. Baseline entries
|
|
269
|
+
record the best-effort local Git email, while repository review policy remains
|
|
270
|
+
the approval boundary.
|
|
232
271
|
|
|
233
272
|
This lets legacy repos adopt the gate without a 200-finding day-one cleanup.
|
|
234
273
|
|
|
235
274
|
## Quickstart
|
|
236
275
|
|
|
237
276
|
```bash
|
|
238
|
-
# Install
|
|
239
|
-
pip install secure-code-agent
|
|
277
|
+
# Install the orchestrator with its pinned default Bandit + pip-audit toolchain
|
|
278
|
+
pip install 'secure-code-agent[required-scanners]'
|
|
240
279
|
|
|
241
280
|
# Initialize agent standards files for your AI coding tools
|
|
242
281
|
secure-code-agent --init-agent-standards \
|
|
@@ -251,14 +290,18 @@ secure-code-agent --config secure-code-agent.json \
|
|
|
251
290
|
--comment-output secure-code-pr-comment.md \
|
|
252
291
|
--prompt-output secure-code-remediation-prompt.md
|
|
253
292
|
|
|
254
|
-
# Audit only changed files since main
|
|
255
|
-
secure-code-agent --changed-only main...HEAD --fail-on-new
|
|
256
|
-
|
|
257
293
|
# Ingest external scanner SARIF (CodeQL, Snyk, Trivy, etc.)
|
|
258
294
|
secure-code-agent --sarif-import codeql-results.sarif \
|
|
259
295
|
--sarif-import snyk-results.sarif
|
|
260
296
|
```
|
|
261
297
|
|
|
298
|
+
`--changed-only` is reserved but not yet safely implemented. Passing it fails
|
|
299
|
+
with exit code 2 so a caller cannot accidentally treat an unscoped audit as a
|
|
300
|
+
changed-file audit.
|
|
301
|
+
|
|
302
|
+
The current orchestrator accepts one repository root per invocation. Multiple
|
|
303
|
+
positional roots fail with exit code 2 instead of silently ignoring coverage.
|
|
304
|
+
|
|
262
305
|
## Invokable skill / slash command
|
|
263
306
|
|
|
264
307
|
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 +315,19 @@ For agents that support invokable skills, this repo ships a portable skill under
|
|
|
272
315
|
## GitHub Action
|
|
273
316
|
|
|
274
317
|
```yaml
|
|
275
|
-
- uses: marshallguillory86/secure-code-agent@v0.
|
|
318
|
+
- uses: marshallguillory86/secure-code-agent@v0.3.0
|
|
276
319
|
with:
|
|
277
320
|
config: secure-code-agent.json
|
|
278
|
-
changed-only: main...HEAD
|
|
279
321
|
fail-on-gate: true
|
|
280
322
|
```
|
|
281
323
|
|
|
282
|
-
The action
|
|
324
|
+
The action installs the exact source bundled with the referenced action plus
|
|
325
|
+
the pinned `required-scanners` extra (Bandit and pip-audit),
|
|
326
|
+
emits Markdown, JSON, SARIF, PR-comment, and remediation artifacts, and uploads
|
|
327
|
+
SARIF by default. The calling workflow must grant `security-events: write` for
|
|
328
|
+
SARIF upload. Pin production usage to a full commit SHA; the version tag above
|
|
329
|
+
is shown for readability. See [`action.yml`](action.yml) and
|
|
330
|
+
[`examples/github-actions/`](examples/github-actions/) for full workflows.
|
|
283
331
|
|
|
284
332
|
## What this is NOT
|
|
285
333
|
|
|
@@ -294,7 +342,7 @@ The action uploads SARIF to GitHub Code Scanning by default. See [`action.yml`](
|
|
|
294
342
|
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
343
|
2. **Bounded scope.** The remediation prompt explicitly forbids touching crypto, auth, validation, logging, and tests.
|
|
296
344
|
3. **Standards-anchored.** Five public standards (NIST / OWASP-x3 / CWE) — no invented taxonomy.
|
|
297
|
-
4. **
|
|
345
|
+
4. **Stable finding identity.** CWE, normalized path, and normalized evidence form the baseline fingerprint. Cross-scanner score deduplication remains future work.
|
|
298
346
|
5. **No vendor lock-in.** Markdown, JSON, SARIF, plain files. Pipe anywhere.
|
|
299
347
|
6. **CI-first, local-first.** Same binary in pre-commit, local CI, GitHub Actions, GitLab, Buildkite.
|
|
300
348
|
|
|
@@ -302,7 +350,11 @@ Full design philosophy in [`docs/design.md`](docs/design.md).
|
|
|
302
350
|
|
|
303
351
|
## Documentation
|
|
304
352
|
|
|
353
|
+
- [`docs/product-intent.md`](docs/product-intent.md) — Why this exists, who it serves, what it refuses to become
|
|
354
|
+
- [`docs/decisions.md`](docs/decisions.md) — Decision register: rulings the code alone cannot answer
|
|
305
355
|
- [`docs/design.md`](docs/design.md) — Architecture + non-goals + scanner protocol
|
|
356
|
+
- [`docs/architecture.md`](docs/architecture.md) — Audit of the system as built + remediation sequence
|
|
357
|
+
- [`docs/release-blockers.md`](docs/release-blockers.md) — Open v0.3.0 release blockers (do not tag until closed)
|
|
306
358
|
- [`docs/standards.md`](docs/standards.md) — NIST SSDF / OWASP / CWE / Scorecard / SARIF citations
|
|
307
359
|
- [`docs/scoring.md`](docs/scoring.md) — Weighting model + worked examples
|
|
308
360
|
- [`docs/scanners.md`](docs/scanners.md) — Per-scanner integrations + caveats
|
|
@@ -312,7 +364,8 @@ Full design philosophy in [`docs/design.md`](docs/design.md).
|
|
|
312
364
|
## Versioning
|
|
313
365
|
|
|
314
366
|
- **Semver.** v0.x is pre-1.0 — the config schema may evolve. v1.0 locks it.
|
|
315
|
-
- **SARIF 2.1.0** output is
|
|
367
|
+
- **SARIF 2.1.0-shaped** output is structurally unit-tested and round-tripped;
|
|
368
|
+
full OASIS schema validation is not yet part of CI.
|
|
316
369
|
|
|
317
370
|
## Get in touch
|
|
318
371
|
|
|
@@ -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.4.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,49 @@ 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
|
+
# njsscan is in the floor (D12) and is a Python package carrying its own
|
|
51
|
+
# rules, so a JavaScript repository can be scanned on a host with no Node.
|
|
52
|
+
# Pinned for the same reason as the rest: the floor is a claim about what
|
|
53
|
+
# ran, and an unpinned ruleset makes that claim unreproducible.
|
|
54
|
+
"njsscan==1.0.0",
|
|
55
|
+
]
|
|
56
|
+
# The remaining Python-packaged scanners. Bounded rather than pinned because
|
|
57
|
+
# they are optional; the coverage gate does not require them by default.
|
|
58
|
+
# Scanners distributed as standalone binaries or through another language's
|
|
59
|
+
# package manager (gitleaks, gosec, hadolint, npm, osv-scanner, rubocop,
|
|
60
|
+
# scorecard, trivy, trufflehog) cannot be installed from PyPI — see
|
|
61
|
+
# docs/scanners.md for their install commands and the SARIF-import route.
|
|
62
|
+
python-scanners = [
|
|
63
|
+
"secure-code-agent[required-scanners]",
|
|
64
|
+
"semgrep>=1.172,<2",
|
|
65
|
+
"checkov>=3.3,<4",
|
|
66
|
+
]
|
|
67
|
+
# Includes required-scanners so a fresh checkout can run its own audit.
|
|
68
|
+
# Ruff is pinned exactly: `ruff format --check` is a CI gate, and an
|
|
69
|
+
# unpinned formatter turns every upstream release into a possible red build
|
|
70
|
+
# with no change in this repository. Same reasoning as required-scanners.
|
|
42
71
|
dev = [
|
|
72
|
+
"secure-code-agent[required-scanners]",
|
|
43
73
|
"pytest>=8.0",
|
|
44
74
|
"pytest-cov>=4.0",
|
|
45
|
-
"ruff
|
|
75
|
+
"ruff==0.16.2",
|
|
46
76
|
]
|
|
47
77
|
|
|
48
78
|
[project.scripts]
|
|
@@ -65,6 +95,15 @@ secure_code_audit = ["data/*.json", "data/*.yaml"]
|
|
|
65
95
|
[tool.ruff]
|
|
66
96
|
line-length = 100
|
|
67
97
|
target-version = "py310"
|
|
98
|
+
# Ruff 0.16 formats Python blocks embedded in Markdown. Our docs use those
|
|
99
|
+
# blocks illustratively — excerpted statements, aligned-comment schemas — and
|
|
100
|
+
# reformatting them damages the documentation without improving any code.
|
|
101
|
+
extend-exclude = [
|
|
102
|
+
"**/*.md",
|
|
103
|
+
# Scanner fixtures are deliberately vulnerable sample code. Linting them
|
|
104
|
+
# would demand we fix the very patterns the rules exist to detect.
|
|
105
|
+
"tests/fixtures/**",
|
|
106
|
+
]
|
|
68
107
|
|
|
69
108
|
[tool.ruff.lint]
|
|
70
109
|
select = ["E", "F", "I", "B", "UP", "SIM", "C4"]
|