一键导入
agentic-security-add-scan-rule
Walk through the six-step recipe for adding a new SAST detector — pick the module, export scan*(), wire, fixture, test.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Walk through the six-step recipe for adding a new SAST detector — pick the module, export scan*(), wire, fixture, test.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Guard risky writes. Activate before eval/exec, string-concat SQL, md5/sha1/DES/RC4 crypto, or auth/jwt/oauth/cipher.
Explain or teach a vuln. Activate on a CVE/GHSA id, "what is this vuln", "explain finding", or a Socratic walkthrough.
Rotate a leaked secret end-to-end. Activate on leaked API key, exposed credential, or pushed-secret reports.
Run the security scanner. Activate on ship / deploy / launch / "is this safe?" — before the deploy happens.
Privacy review before handling user data. Activate on PII/PHI/PCI shapes (email, SSN, CC, MRN). Writes DATA_FLOW.md.
Apply a remediation patch via the deterministic MCP toolchain. Activate when user asks to fix a scanner finding.
| name | agentic-security:add-scan-rule |
| description | Walk through the six-step recipe for adding a new SAST detector — pick the module, export scan*(), wire, fixture, test. |
When the user wants the scanner to detect a new vulnerability pattern, follow this workflow. Loaded on-demand, not into every session — see scanner/src/sast/CLAUDE.md for the same content baked into the SAST tree.
| Shape of the rule | Module pattern |
|---|---|
| Language-specific (e.g. Python sink, Kotlin force-unwrap) | Add to or create scanner/src/sast/<language>.js |
| Framework-specific hardening (e.g. Express auth, FastAPI defaults) | scanner/src/sast/<framework>-hardening.js |
| Cross-cutting vuln class (CSRF, prototype pollution, mass assignment) | New top-level scanner/src/sast/<topic>.js |
| Posture annotator (mutates existing findings) | scanner/src/posture/ — see scanner/src/posture/CLAUDE.md |
If the rule is "X but with light cross-file context," still emit from sast/ and let the cross-file annotators in posture/cross-lang-*.js enrich it. Don't put cross-file logic into the SAST module.
Write the detector. Export a scan<Name>(fileContents, opts) function returning Finding[]. Required fields: id, severity, file, line, vuln, cwe, description, remediation. Set family and parser when you know them; posture/finding-defaults.js backfills, but detector-set wins.
Wire into the engine. Open scanner/src/engine.js. Add the import to the existing block (alphabetical) and call it inside runFullScan next to similar rules. Append the results to finalFindings.
Add the fixture pair. Create scanner/test/fixtures/<rule-name>/vulnerable/ and scanner/test/fixtures/<rule-name>/clean/. Each holds one small file demonstrating the vulnerable / fixed shape. The vulnerable file must produce a finding; the clean file must not.
Add the test. Either extend an existing topical test (e.g. test/python-sinks.test.js) or create test/<rule-name>.test.js modelled on the smallest existing test. Wire the file into the matching scoped script in scanner/package.json (test:sast, test:dataflow, etc.).
Verify. Use the scoped script that covers what you touched (npm run test:smoke, npm run test:sast, npm run test:dataflow, …). Always include npm run test:lifecycle — it catches a rule that ships without an engine.js wire-up. The full npm test is the CI gate but is usually overkill for a single new rule.
Rebuild the bundle if anything outside src/ will consume it. npm run build. Then npm run smoke to check the CLI path. If bundle smoke disagrees with unit-test smoke, you have an engine.js wiring miss — unit tests run against src/ directly, but the bundle re-binds at build time.
blankComments() from scanner/src/sast/_comment-strip.js before scanning a file body. Otherwise a vuln pattern inside a // example: block fires.exec('ping ' + req.body.host, …)), the match offset and the readable sink line can diverge. Use the actual sink expression for the finding's snippet, not lines[regex.lastIndex].critical without strong evidence. annotateExploitability will lift high to critical when production context warrants. A flood of false criticals drowns real ones.family, the rule probably covers too much. Split it.scanner/src/sast/bench-shape/ and stays gated by AGENTIC_SECURITY_BENCH_SHAPE=1. Never make a production rule depend on bench shape.After step 5 the lifecycle test should pass with the new file. If it fails with "exported X has no external call site," you forgot step 2 — go back and add the import + call in engine.js.
If npm run smoke reports the new rule fires in clean/ (false positive on the clean fixture), tighten the regex/AST match. If it doesn't fire in vulnerable/, your match shape is wrong — look at the actual fixture content with the actual detector and trace where it diverges.
You're done when:
npm run test:lifecycle passesnpm run build && npm run smoke produces the same finding count via the CLI as the unit test reported