agent-threat-rules 0.4.0 → 1.0.1

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 (173) hide show
  1. package/README.md +161 -52
  2. package/dist/badge.d.ts.map +1 -1
  3. package/dist/badge.js +6 -1
  4. package/dist/badge.js.map +1 -1
  5. package/dist/cli/scan-handler.d.ts +19 -0
  6. package/dist/cli/scan-handler.d.ts.map +1 -0
  7. package/dist/cli/scan-handler.js +257 -0
  8. package/dist/cli/scan-handler.js.map +1 -0
  9. package/dist/cli.js +44 -86
  10. package/dist/cli.js.map +1 -1
  11. package/dist/content-hash.d.ts +7 -0
  12. package/dist/content-hash.d.ts.map +1 -0
  13. package/dist/content-hash.js +10 -0
  14. package/dist/content-hash.js.map +1 -0
  15. package/dist/converters/generic-regex.d.ts +37 -0
  16. package/dist/converters/generic-regex.d.ts.map +1 -0
  17. package/dist/converters/generic-regex.js +59 -0
  18. package/dist/converters/generic-regex.js.map +1 -0
  19. package/dist/converters/index.d.ts +4 -0
  20. package/dist/converters/index.d.ts.map +1 -1
  21. package/dist/converters/index.js +2 -0
  22. package/dist/converters/index.js.map +1 -1
  23. package/dist/converters/sarif.d.ts +18 -0
  24. package/dist/converters/sarif.d.ts.map +1 -0
  25. package/dist/converters/sarif.js +142 -0
  26. package/dist/converters/sarif.js.map +1 -0
  27. package/dist/engine.d.ts +21 -1
  28. package/dist/engine.d.ts.map +1 -1
  29. package/dist/engine.js +215 -4
  30. package/dist/engine.js.map +1 -1
  31. package/dist/eval/pint-corpus.d.ts.map +1 -1
  32. package/dist/eval/pint-corpus.js +6 -2
  33. package/dist/eval/pint-corpus.js.map +1 -1
  34. package/dist/eval/rule-corpus.js +489 -489
  35. package/dist/eval/rule-corpus.js.map +1 -1
  36. package/dist/eval/skill-benchmark.d.ts +66 -0
  37. package/dist/eval/skill-benchmark.d.ts.map +1 -0
  38. package/dist/eval/skill-benchmark.js +194 -0
  39. package/dist/eval/skill-benchmark.js.map +1 -0
  40. package/dist/index.d.ts +4 -2
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +3 -1
  43. package/dist/index.js.map +1 -1
  44. package/dist/layer-integration.d.ts.map +1 -1
  45. package/dist/layer-integration.js +2 -0
  46. package/dist/layer-integration.js.map +1 -1
  47. package/dist/loader.d.ts +0 -3
  48. package/dist/loader.d.ts.map +1 -1
  49. package/dist/loader.js +7 -2
  50. package/dist/loader.js.map +1 -1
  51. package/dist/mcp-server.d.ts.map +1 -1
  52. package/dist/mcp-server.js +26 -0
  53. package/dist/mcp-server.js.map +1 -1
  54. package/dist/mcp-tools/scan-skill.d.ts +17 -0
  55. package/dist/mcp-tools/scan-skill.d.ts.map +1 -0
  56. package/dist/mcp-tools/scan-skill.js +65 -0
  57. package/dist/mcp-tools/scan-skill.js.map +1 -0
  58. package/dist/mcp-tools/validate.d.ts.map +1 -1
  59. package/dist/mcp-tools/validate.js +6 -0
  60. package/dist/mcp-tools/validate.js.map +1 -1
  61. package/dist/shadow-evaluator.d.ts.map +1 -1
  62. package/dist/shadow-evaluator.js +1 -0
  63. package/dist/shadow-evaluator.js.map +1 -1
  64. package/dist/tier0-invariant.d.ts.map +1 -1
  65. package/dist/tier0-invariant.js +1 -0
  66. package/dist/tier0-invariant.js.map +1 -1
  67. package/dist/tier1-blacklist.d.ts.map +1 -1
  68. package/dist/tier1-blacklist.js +1 -0
  69. package/dist/tier1-blacklist.js.map +1 -1
  70. package/dist/types.d.ts +23 -1
  71. package/dist/types.d.ts.map +1 -1
  72. package/package.json +3 -1
  73. package/rules/agent-manipulation/{ATR-2026-030-cross-agent-attack.yaml → ATR-2026-00030-cross-agent-attack.yaml} +3 -1
  74. package/rules/agent-manipulation/{ATR-2026-032-goal-hijacking.yaml → ATR-2026-00032-goal-hijacking.yaml} +3 -1
  75. package/rules/agent-manipulation/{ATR-2026-074-cross-agent-privilege-escalation.yaml → ATR-2026-00074-cross-agent-privilege-escalation.yaml} +3 -1
  76. package/rules/agent-manipulation/{ATR-2026-076-inter-agent-message-spoofing.yaml → ATR-2026-00076-inter-agent-message-spoofing.yaml} +3 -1
  77. package/rules/agent-manipulation/{ATR-2026-077-human-trust-exploitation.yaml → ATR-2026-00077-human-trust-exploitation.yaml} +3 -1
  78. package/rules/agent-manipulation/{ATR-2026-108-consensus-sybil-attack.yaml → ATR-2026-00108-consensus-sybil-attack.yaml} +3 -1
  79. package/rules/agent-manipulation/{ATR-2026-116-a2a-message-validation.yaml → ATR-2026-00116-a2a-message-validation.yaml} +4 -2
  80. package/rules/agent-manipulation/{ATR-2026-117-agent-identity-spoofing.yaml → ATR-2026-00117-agent-identity-spoofing.yaml} +4 -2
  81. package/rules/agent-manipulation/{ATR-2026-118-approval-fatigue.yaml → ATR-2026-00118-approval-fatigue.yaml} +3 -1
  82. package/rules/agent-manipulation/{ATR-2026-119-social-engineering-via-agent.yaml → ATR-2026-00119-social-engineering-via-agent.yaml} +3 -1
  83. package/rules/agent-manipulation/ATR-2026-00132-casual-authority-escalation.yaml +105 -0
  84. package/rules/agent-manipulation/ATR-2026-00139-casual-authority-redirect.yaml +53 -0
  85. package/rules/context-exfiltration/{ATR-2026-020-system-prompt-leak.yaml → ATR-2026-00020-system-prompt-leak.yaml} +3 -1
  86. package/rules/context-exfiltration/{ATR-2026-021-api-key-exposure.yaml → ATR-2026-00021-api-key-exposure.yaml} +3 -1
  87. package/rules/context-exfiltration/{ATR-2026-075-agent-memory-manipulation.yaml → ATR-2026-00075-agent-memory-manipulation.yaml} +3 -1
  88. package/rules/context-exfiltration/{ATR-2026-102-disguised-analytics-exfiltration.yaml → ATR-2026-00102-disguised-analytics-exfiltration.yaml} +3 -1
  89. package/rules/context-exfiltration/{ATR-2026-113-credential-theft.yaml → ATR-2026-00113-credential-theft.yaml} +3 -1
  90. package/rules/context-exfiltration/{ATR-2026-114-oauth-token-abuse.yaml → ATR-2026-00114-oauth-token-abuse.yaml} +3 -1
  91. package/rules/context-exfiltration/{ATR-2026-115-env-var-harvesting.yaml → ATR-2026-00115-env-var-harvesting.yaml} +3 -1
  92. package/rules/context-exfiltration/ATR-2026-00136-tool-response-data-piggyback.yaml +100 -0
  93. package/rules/context-exfiltration/ATR-2026-00141-example-format-key-leak.yaml +52 -0
  94. package/rules/context-exfiltration/ATR-2026-00142-piggyback-transition-words.yaml +55 -0
  95. package/rules/context-exfiltration/ATR-2026-00145-obfuscated-key-disclosure.yaml +49 -0
  96. package/rules/context-exfiltration/ATR-2026-00146-env-var-existence-probe.yaml +49 -0
  97. package/rules/data-poisoning/{ATR-2026-070-data-poisoning.yaml → ATR-2026-00070-data-poisoning.yaml} +3 -1
  98. package/rules/excessive-autonomy/{ATR-2026-050-runaway-agent-loop.yaml → ATR-2026-00050-runaway-agent-loop.yaml} +3 -1
  99. package/rules/excessive-autonomy/{ATR-2026-051-resource-exhaustion.yaml → ATR-2026-00051-resource-exhaustion.yaml} +3 -1
  100. package/rules/excessive-autonomy/{ATR-2026-052-cascading-failure.yaml → ATR-2026-00052-cascading-failure.yaml} +3 -1
  101. package/rules/excessive-autonomy/{ATR-2026-098-unauthorized-financial-action.yaml → ATR-2026-00098-unauthorized-financial-action.yaml} +3 -1
  102. package/rules/excessive-autonomy/{ATR-2026-099-high-risk-tool-gate.yaml → ATR-2026-00099-high-risk-tool-gate.yaml} +3 -1
  103. package/rules/model-security/{ATR-2026-072-model-behavior-extraction.yaml → ATR-2026-00072-model-behavior-extraction.yaml} +3 -1
  104. package/rules/model-security/{ATR-2026-073-malicious-finetuning-data.yaml → ATR-2026-00073-malicious-finetuning-data.yaml} +3 -1
  105. package/rules/privilege-escalation/{ATR-2026-040-privilege-escalation.yaml → ATR-2026-00040-privilege-escalation.yaml} +3 -1
  106. package/rules/privilege-escalation/{ATR-2026-041-scope-creep.yaml → ATR-2026-00041-scope-creep.yaml} +3 -1
  107. package/rules/privilege-escalation/{ATR-2026-107-delayed-execution-bypass.yaml → ATR-2026-00107-delayed-execution-bypass.yaml} +3 -1
  108. package/rules/privilege-escalation/{ATR-2026-110-eval-injection.yaml → ATR-2026-00110-eval-injection.yaml} +3 -1
  109. package/rules/privilege-escalation/{ATR-2026-111-shell-escape.yaml → ATR-2026-00111-shell-escape.yaml} +5 -3
  110. package/rules/privilege-escalation/{ATR-2026-112-dynamic-import-exploitation.yaml → ATR-2026-00112-dynamic-import-exploitation.yaml} +3 -1
  111. package/rules/privilege-escalation/ATR-2026-00143-casual-privilege-escalation.yaml +53 -0
  112. package/rules/privilege-escalation/ATR-2026-00144-rationalized-safety-bypass.yaml +49 -0
  113. package/rules/prompt-injection/{ATR-2026-001-direct-prompt-injection.yaml → ATR-2026-00001-direct-prompt-injection.yaml} +3 -1
  114. package/rules/prompt-injection/{ATR-2026-002-indirect-prompt-injection.yaml → ATR-2026-00002-indirect-prompt-injection.yaml} +3 -1
  115. package/rules/prompt-injection/{ATR-2026-003-jailbreak-attempt.yaml → ATR-2026-00003-jailbreak-attempt.yaml} +3 -1
  116. package/rules/prompt-injection/{ATR-2026-004-system-prompt-override.yaml → ATR-2026-00004-system-prompt-override.yaml} +3 -1
  117. package/rules/prompt-injection/{ATR-2026-005-multi-turn-injection.yaml → ATR-2026-00005-multi-turn-injection.yaml} +3 -1
  118. package/rules/prompt-injection/{ATR-2026-080-encoding-evasion.yaml → ATR-2026-00080-encoding-evasion.yaml} +3 -1
  119. package/rules/prompt-injection/{ATR-2026-081-semantic-multi-turn.yaml → ATR-2026-00081-semantic-multi-turn.yaml} +3 -1
  120. package/rules/prompt-injection/{ATR-2026-082-fingerprint-evasion.yaml → ATR-2026-00082-fingerprint-evasion.yaml} +3 -1
  121. package/rules/prompt-injection/{ATR-2026-083-indirect-tool-injection.yaml → ATR-2026-00083-indirect-tool-injection.yaml} +3 -1
  122. package/rules/prompt-injection/{ATR-2026-084-structured-data-injection.yaml → ATR-2026-00084-structured-data-injection.yaml} +3 -1
  123. package/rules/prompt-injection/{ATR-2026-085-audit-evasion.yaml → ATR-2026-00085-audit-evasion.yaml} +3 -1
  124. package/rules/prompt-injection/{ATR-2026-086-visual-spoofing.yaml → ATR-2026-00086-visual-spoofing.yaml} +3 -1
  125. package/rules/prompt-injection/{ATR-2026-087-rule-probing.yaml → ATR-2026-00087-rule-probing.yaml} +3 -1
  126. package/rules/prompt-injection/{ATR-2026-088-adaptive-countermeasure.yaml → ATR-2026-00088-adaptive-countermeasure.yaml} +3 -1
  127. package/rules/prompt-injection/{ATR-2026-089-polymorphic-skill.yaml → ATR-2026-00089-polymorphic-skill.yaml} +3 -1
  128. package/rules/prompt-injection/{ATR-2026-090-threat-intel-exfil.yaml → ATR-2026-00090-threat-intel-exfil.yaml} +3 -1
  129. package/rules/prompt-injection/{ATR-2026-091-nested-payload.yaml → ATR-2026-00091-nested-payload.yaml} +3 -1
  130. package/rules/prompt-injection/{ATR-2026-092-consensus-poisoning.yaml → ATR-2026-00092-consensus-poisoning.yaml} +3 -1
  131. package/rules/prompt-injection/{ATR-2026-093-gradual-escalation.yaml → ATR-2026-00093-gradual-escalation.yaml} +3 -1
  132. package/rules/prompt-injection/{ATR-2026-094-audit-bypass.yaml → ATR-2026-00094-audit-bypass.yaml} +3 -1
  133. package/rules/prompt-injection/{ATR-2026-097-cjk-injection-patterns.yaml → ATR-2026-00097-cjk-injection-patterns.yaml} +3 -1
  134. package/rules/prompt-injection/{ATR-2026-104-persona-hijacking.yaml → ATR-2026-00104-persona-hijacking.yaml} +3 -1
  135. package/rules/prompt-injection/ATR-2026-00130-indirect-authority-claim.yaml +103 -0
  136. package/rules/prompt-injection/ATR-2026-00131-fictional-academic-framing.yaml +99 -0
  137. package/rules/prompt-injection/ATR-2026-00133-paraphrase-injection.yaml +117 -0
  138. package/rules/prompt-injection/ATR-2026-00137-authority-claim-injection.yaml +52 -0
  139. package/rules/prompt-injection/ATR-2026-00138-fictional-framing-bypass.yaml +51 -0
  140. package/rules/prompt-injection/ATR-2026-00140-indirect-reference-reversal.yaml +52 -0
  141. package/rules/prompt-injection/ATR-2026-00148-language-switch-injection.yaml +71 -0
  142. package/rules/skill-compromise/{ATR-2026-060-skill-impersonation.yaml → ATR-2026-00060-skill-impersonation.yaml} +3 -1
  143. package/rules/skill-compromise/{ATR-2026-061-description-behavior-mismatch.yaml → ATR-2026-00061-description-behavior-mismatch.yaml} +3 -1
  144. package/rules/skill-compromise/{ATR-2026-062-hidden-capability.yaml → ATR-2026-00062-hidden-capability.yaml} +3 -1
  145. package/rules/skill-compromise/{ATR-2026-063-skill-chain-attack.yaml → ATR-2026-00063-skill-chain-attack.yaml} +3 -1
  146. package/rules/skill-compromise/{ATR-2026-064-over-permissioned-skill.yaml → ATR-2026-00064-over-permissioned-skill.yaml} +3 -1
  147. package/rules/skill-compromise/{ATR-2026-065-skill-update-attack.yaml → ATR-2026-00065-skill-update-attack.yaml} +3 -1
  148. package/rules/skill-compromise/{ATR-2026-066-parameter-injection.yaml → ATR-2026-00066-parameter-injection.yaml} +3 -1
  149. package/rules/skill-compromise/ATR-2026-00120-skill-instruction-injection.yaml +121 -0
  150. package/rules/skill-compromise/ATR-2026-00121-skill-dangerous-script.yaml +165 -0
  151. package/rules/skill-compromise/ATR-2026-00122-skill-weaponized-instruction.yaml +114 -0
  152. package/rules/skill-compromise/ATR-2026-00123-skill-overreach-permissions.yaml +118 -0
  153. package/rules/skill-compromise/ATR-2026-00124-skill-name-squatting.yaml +98 -0
  154. package/rules/skill-compromise/ATR-2026-00125-context-poisoning-compaction.yaml +93 -0
  155. package/rules/skill-compromise/ATR-2026-00126-skill-rug-pull-setup.yaml +99 -0
  156. package/rules/skill-compromise/ATR-2026-00127-subcommand-overflow.yaml +74 -0
  157. package/rules/skill-compromise/ATR-2026-00128-html-comment-hidden-payload.yaml +79 -0
  158. package/rules/skill-compromise/ATR-2026-00129-unicode-smuggling.yaml +73 -0
  159. package/rules/skill-compromise/ATR-2026-00134-fork-claim-impersonation.yaml +93 -0
  160. package/rules/skill-compromise/ATR-2026-00135-exfil-url-in-instructions.yaml +82 -0
  161. package/rules/skill-compromise/ATR-2026-00147-fork-impersonation.yaml +48 -0
  162. package/rules/tool-poisoning/{ATR-2026-010-mcp-malicious-response.yaml → ATR-2026-00010-mcp-malicious-response.yaml} +3 -1
  163. package/rules/tool-poisoning/{ATR-2026-011-tool-output-injection.yaml → ATR-2026-00011-tool-output-injection.yaml} +3 -1
  164. package/rules/tool-poisoning/{ATR-2026-012-unauthorized-tool-call.yaml → ATR-2026-00012-unauthorized-tool-call.yaml} +3 -1
  165. package/rules/tool-poisoning/{ATR-2026-013-tool-ssrf.yaml → ATR-2026-00013-tool-ssrf.yaml} +3 -1
  166. package/rules/tool-poisoning/{ATR-2026-095-supply-chain-poisoning.yaml → ATR-2026-00095-supply-chain-poisoning.yaml} +3 -1
  167. package/rules/tool-poisoning/{ATR-2026-096-registry-poisoning.yaml → ATR-2026-00096-registry-poisoning.yaml} +3 -1
  168. package/rules/tool-poisoning/{ATR-2026-100-consent-bypass-instruction.yaml → ATR-2026-00100-consent-bypass-instruction.yaml} +3 -1
  169. package/rules/tool-poisoning/{ATR-2026-101-trust-escalation-override.yaml → ATR-2026-00101-trust-escalation-override.yaml} +3 -1
  170. package/rules/tool-poisoning/{ATR-2026-103-hidden-safety-bypass-instruction.yaml → ATR-2026-00103-hidden-safety-bypass-instruction.yaml} +3 -1
  171. package/rules/tool-poisoning/{ATR-2026-105-silent-action-concealment.yaml → ATR-2026-00105-silent-action-concealment.yaml} +3 -1
  172. package/rules/tool-poisoning/{ATR-2026-106-schema-description-contradiction.yaml → ATR-2026-00106-schema-description-contradiction.yaml} +3 -1
  173. package/spec/atr-schema.yaml +32 -3
package/README.md CHANGED
@@ -9,10 +9,11 @@ AI Agent 威脅偵測規則 -- 開源、社群驅動
9
9
  <br />
10
10
 
11
11
  [![License](https://img.shields.io/badge/license-MIT-brightgreen?style=flat-square)](LICENSE)
12
- [![Rules](https://img.shields.io/badge/rules-61-blue?style=flat-square)](#what-atr-detects)
13
- [![Tests](https://img.shields.io/badge/tests-257_passing-green?style=flat-square)](#ecosystem)
12
+ [![Rules](https://img.shields.io/badge/rules-100-blue?style=flat-square)](#what-atr-detects)
13
+ [![Tests](https://img.shields.io/badge/tests-278_passing-green?style=flat-square)](#ecosystem)
14
14
  [![PINT Recall](https://img.shields.io/badge/PINT_recall-62.7%25-green?style=flat-square)](#evaluation)
15
- [![Status](https://img.shields.io/badge/status-v0.3.1-yellow?style=flat-square)](#roadmap)
15
+ [![OWASP](https://img.shields.io/badge/OWASP_Agentic_Top_10-10%2F10-brightgreen?style=flat-square)](#standards-coverage)
16
+ [![Status](https://img.shields.io/badge/status-v1.0.0-brightgreen?style=flat-square)](#roadmap)
16
17
 
17
18
  </div>
18
19
 
@@ -22,38 +23,86 @@ AI assistants (ChatGPT, Claude, Copilot) now browse the web, run code, and use e
22
23
 
23
24
  AI 助理現在可以瀏覽網頁、執行程式碼、使用外部工具。攻擊者可以欺騙它們洩漏資料、執行惡意指令、繞過安全限制。**ATR 是一套開放的偵測規則,專門識別這些攻擊 -- 像防毒軟體的病毒碼,但對象是 AI Agent。**
24
25
 
25
- ```bash
26
- # Quick install (macOS / Linux)
27
- curl -fsSL https://raw.githubusercontent.com/Agent-Threat-Rule/agent-threat-rules/main/scripts/install.sh | sh
26
+ ### Where ATR fits in the AI agent security stack
27
+
28
+ | Layer | What it does | Project |
29
+ |-------|-------------|---------|
30
+ | **Standards** | Define threat categories | [SAFE-MCP](https://openssf.org/) (OpenSSF, $12.5M) |
31
+ | **Taxonomy** | Enumerate attack surfaces | [OWASP Agentic Top 10](https://genai.owasp.org/) |
32
+ | **Detection rules** | Match threats in real time | **ATR** (this project) |
33
+ | **Enforcement** | Block, alert, quarantine | [PanGuard](https://panguard.ai), your SIEM, your pipeline |
34
+
35
+ ATR maps to **10/10 OWASP Agentic Top 10 categories** ([full mapping](docs/OWASP-MAPPING.md)) and **91.8% of SAFE-MCP techniques** ([full mapping](docs/SAFE-MCP-MAPPING.md)).
36
+
37
+ ### Who uses ATR
38
+
39
+ | Organization | Integration | Reference |
40
+ |---|---|---|
41
+ | **Cisco AI Defense** | 34 ATR rules merged into official skill-scanner | [PR #79](https://github.com/cisco-ai-defense/skill-scanner/pull/79) |
42
+ | **OWASP** | ASI01-ASI10 attack examples + detection strategies | [PR #814](https://github.com/OWASP/www-project-top-10-for-large-language-model-applications/pull/814) |
43
+ | **OWASP Agentic AI Top 10** | Full vulnerability mapping | [PR #14](https://github.com/precize/Agentic-AI-Top10-Vulnerability/pull/14) (merged) |
44
+
45
+ > ATR rules are consumed as a standard -- not a product. MIT licensed, auto-updated via npm, zero strings attached.
46
+
47
+ ### Ecosystem scan (53,377 skills)
48
+
49
+ We scanned the two largest MCP skill registries: OpenClaw (50,285) and Skills.sh (3,115).
50
+
51
+ | Metric | Number |
52
+ |--------|--------|
53
+ | Skills scanned | **53,377** |
54
+ | Clean | 47,438 (88.87%) |
55
+ | **CRITICAL** | 3,255 |
56
+ | **HIGH** | 2,656 |
57
+ | **MEDIUM** | 28 |
58
+
59
+ Raw data: [mega-scan-report.json](data/mega-scan-report.json) / [ecosystem-report.csv](data/clawhub-scan/ecosystem-report.csv)
28
60
 
29
- # Or install manually
61
+ ```bash
30
62
  npm install -g agent-threat-rules
31
63
 
32
- atr scan events.json # scan agent traffic for threats
33
- atr init # setup Claude Code guard hook
34
- atr mcp # start MCP server for IDE
64
+ atr scan skill.md # scan a SKILL.md for threats
65
+ atr scan mcp-config.json # scan MCP events for threats
66
+ atr scan skill.md --sarif # output SARIF v2.1.0 for GitHub Security tab
67
+ atr convert generic-regex # export 100 rules as JSON (685 regex patterns)
68
+ atr convert splunk # export to Splunk SPL
69
+ atr convert elastic # export to Elasticsearch Query DSL
35
70
  atr stats # show rule collection stats
71
+ atr mcp # start MCP server for IDE integration
36
72
  ```
37
73
 
74
+ ### GitHub Action (CI/CD)
75
+
76
+ ```yaml
77
+ # .github/workflows/atr-scan.yml
78
+ - uses: Agent-Threat-Rule/agent-threat-rules@v1
79
+ with:
80
+ path: '.' # scan SKILL.md and MCP configs in repo
81
+ severity: 'medium' # minimum severity to report
82
+ upload-sarif: 'true' # results appear in GitHub Security tab
83
+ ```
84
+
85
+ One line. Zero config. SARIF results in your Security tab.
86
+
38
87
  **For security professionals:** ATR is the [Sigma](https://github.com/SigmaHQ/sigma)/[YARA](https://github.com/VirusTotal/yara) equivalent for AI agent threats -- YAML-based rules with regex matching, behavioral fingerprinting, LLM-as-judge analysis, and mappings to [OWASP LLM Top 10](https://owasp.org/www-project-top-10-for-large-language-model-applications/), [OWASP Agentic Top 10](https://genai.owasp.org/resource/owasp-top-10-for-agentic-applications-for-2026/), and [MITRE ATLAS](https://atlas.mitre.org/).
39
88
 
40
89
  ---
41
90
 
42
91
  ## What ATR Detects
43
92
 
44
- 61 rules across 9 categories, mapped to real CVEs:
93
+ 100 rules across 9 categories, mapped to real CVEs:
45
94
 
46
95
  | Category | What it catches | Rules | Real CVEs |
47
96
  |----------|----------------|-------|-----------|
48
- | **Prompt Injection** | "Ignore previous instructions", persona hijacking, encoded payloads, [CJK attacks](rules/prompt-injection/) | 22 | CVE-2025-53773, CVE-2025-32711 |
97
+ | **Prompt Injection** | "Ignore previous instructions", persona hijacking, encoded payloads, CJK attacks | 22 | CVE-2025-53773, CVE-2025-32711 |
49
98
  | **Tool Poisoning** | Malicious MCP responses, consent bypass, hidden LLM instructions, schema contradictions | 11 | CVE-2025-68143/68144/68145 |
50
- | **Skill Compromise** | Typosquatting, description-behavior mismatch, supply chain attacks | 7 | CVE-2025-59536 |
51
- | **Agent Manipulation** | Cross-agent attacks, goal hijacking, Sybil consensus attacks | 6 | -- |
99
+ | **Skill Compromise** | Typosquatting, context poisoning, subcommand overflow, rug pull, supply chain attacks | 20 | CVE-2025-59536, CVE-2026-28363 |
100
+ | **Agent Manipulation** | Cross-agent attacks, goal hijacking, Sybil consensus attacks | 10 | -- |
52
101
  | **Excessive Autonomy** | Runaway loops, resource exhaustion, unauthorized financial actions | 5 | -- |
53
- | **Context Exfiltration** | API key leakage, system prompt theft, disguised analytics collection | 4 | CVE-2026-24307 |
54
- | **Privilege Escalation** | Scope creep, delayed execution bypass | 3 | CVE-2026-0628 |
55
- | **Model Security** | Behavior extraction, malicious fine-tuning data | 2 | -- |
56
- | **Data Poisoning** | RAG/knowledge base tampering | 1 | -- |
102
+ | **Context Exfiltration** | API key leakage, system prompt theft, credential harvesting, env variable exfiltration | 15 | CVE-2026-24307 |
103
+ | **Privilege Escalation** | Scope creep, delayed execution bypass, admin function access | 9 | CVE-2026-0628 |
104
+ | **Model Security** | Behavior extraction, malicious fine-tuning data | 5 | -- |
105
+ | **Data Poisoning** | RAG/knowledge base tampering, memory manipulation | 3 | -- |
57
106
 
58
107
  > **Limitations:** Regex catches known patterns, not paraphrased attacks. We publish [evasion tests](LIMITATIONS.md) showing what we can't catch. See [LIMITATIONS.md](LIMITATIONS.md) for honest benchmark numbers including external PINT results.
59
108
 
@@ -63,17 +112,34 @@ atr stats # show rule collection stats
63
112
 
64
113
  We test ATR with our own tests AND external benchmarks we've never seen before:
65
114
 
66
- | Benchmark | Samples | Precision | Recall | F1 |
67
- |-----------|---------|-----------|--------|-----|
68
- | Self-test (own rules' test cases) | 341 | 100% | 99.4% | 99.5% |
69
- | **PINT (external, adversarial)** | **850** | **99.4%** | **39.9%** | **57.0%** |
115
+ | Benchmark | Source | Samples | Precision | Recall |
116
+ |-----------|--------|---------|-----------|--------|
117
+ | Self-test (own test cases) | Internal | 341 | 100% | 88.5% |
118
+ | **PINT (adversarial)** | **Invariant Labs** | **850** | **99.6%** | **61.4%** |
119
+ | **Garak (real-world jailbreaks)** | **NVIDIA** | **666** | -- | **69.7%** |
120
+ | **53K ecosystem scan** | **OpenClaw + Skills.sh** | **53,377** | **99.7%** | -- |
70
121
 
71
122
  ```bash
72
- npm run eval # run self-test evaluation
73
- npm run eval:pint # run external PINT benchmark
123
+ npm run eval # run self-test evaluation
124
+ npm run eval:pint # run external PINT benchmark
125
+ bash scripts/eval-garak.sh # run NVIDIA Garak benchmark (requires: pip install garak)
74
126
  ```
75
127
 
76
- The gap between 99.4% and 39.9% recall is expected -- regex catches known patterns but misses paraphrases and multilingual attacks. See [LIMITATIONS.md](LIMITATIONS.md) for full analysis.
128
+ **What the numbers mean:** ATR regex catches ~62-70% of attacks instantly (< 5ms, $0). The remaining ~30% are paraphrased/persona attacks that need LLM-layer detection. This is by design -- regex is the fast first gate, not the only gate. See [LIMITATIONS.md](LIMITATIONS.md) for full analysis.
129
+
130
+ ---
131
+
132
+ ## Standards Coverage
133
+
134
+ ATR maps to established AI security frameworks so teams can go from "understand the threat" to "detect it" without building rules from scratch.
135
+
136
+ | Framework | Coverage | Mapping |
137
+ |-----------|----------|---------|
138
+ | [OWASP Agentic Top 10](https://genai.owasp.org/resource/owasp-top-10-for-agentic-applications-for-2026/) | **10/10 categories** | [OWASP-MAPPING.md](docs/OWASP-MAPPING.md) |
139
+ | [SAFE-MCP](https://openssf.org/) (OpenSSF) | **78/85 techniques (91.8%)** | [SAFE-MCP-MAPPING.md](docs/SAFE-MCP-MAPPING.md) |
140
+ | [MITRE ATLAS](https://atlas.mitre.org/) | Rule-level references | Per-rule `mitre_ref` field |
141
+
142
+ **Paper:** Pan, Y. (2026). *Agent Threat Rules: A Community-Driven Detection Standard for AI Agent Security Threats.* Zenodo. [doi:10.5281/zenodo.19178002](https://doi.org/10.5281/zenodo.19178002)
77
143
 
78
144
  ---
79
145
 
@@ -81,14 +147,17 @@ The gap between 99.4% and 39.9% recall is expected -- regex catches known patter
81
147
 
82
148
  | Component | Description | Status |
83
149
  |-----------|-------------|--------|
84
- | [TypeScript engine](src/engine.ts) | Reference engine with 5-tier detection | 341 tests passing |
85
- | [Eval framework](src/eval/) | Precision/recall/F1, regression gate, PINT benchmark | v0.3.1 |
150
+ | [TypeScript engine](src/engine.ts) | Reference engine with 5-tier detection | 278 tests passing |
151
+ | [Eval framework](src/eval/) | Precision/recall/F1, regression gate, PINT benchmark | v1.0.0 |
86
152
  | [Python engine (pyATR)](python/) | Local install only (`cd python && pip install -e .`) | 48 tests passing |
153
+ | [GitHub Action](action.yml) | One-line CI scan with SARIF output | **New** |
154
+ | [SARIF converter](src/converters/sarif.ts) | `atr scan --sarif` -- SARIF v2.1.0 for GitHub Security tab | **New** |
155
+ | [Generic regex export](src/converters/generic-regex.ts) | `atr convert generic-regex` -- 685 patterns JSON for any tool | **New** |
87
156
  | [Splunk converter](src/converters/splunk.ts) | `atr convert splunk` -- ATR rules to SPL queries | Shipped |
88
157
  | [Elastic converter](src/converters/elastic.ts) | `atr convert elastic` -- ATR rules to Query DSL | Shipped |
89
158
  | [MCP server](src/mcp-server.ts) | 6 tools for Claude Code, Cursor, Windsurf | Shipped |
90
- | [CLI](src/cli.ts) | scan, validate, test, stats, scaffold, convert | Shipped |
91
- | [CI gate](.github/workflows/eval.yml) | Typecheck + test + eval + validate on every PR | v0.3.0 |
159
+ | [CLI](src/cli.ts) | scan, validate, test, stats, scaffold, convert, badge | Shipped |
160
+ | [CI gate](.github/workflows/eval.yml) | Typecheck + test + eval + validate on every PR | v1.0.0 |
92
161
  | Go engine | High-performance scanner for production pipelines | **Help wanted** |
93
162
 
94
163
  ---
@@ -144,13 +213,22 @@ atr test my-rule.yaml
144
213
 
145
214
  Every rule is a YAML file answering: **what** to detect, **how** to detect it, **what to do**, and **how to test it**. See [examples/how-to-write-a-rule.md](examples/how-to-write-a-rule.md) for a walkthrough, or [spec/atr-schema.yaml](spec/atr-schema.yaml) for the full schema.
146
215
 
147
- ### Export to SIEM
216
+ ### Export rules
148
217
 
149
218
  ```bash
219
+ # For your security platform (100 rules, 685 regex patterns as JSON)
220
+ atr convert generic-regex --output atr-rules.json
221
+
222
+ # For SIEM integration
150
223
  atr convert splunk --output atr-rules.spl
151
224
  atr convert elastic --output atr-rules.json
225
+
226
+ # For GitHub / CI
227
+ atr scan skill.md --sarif > results.sarif
152
228
  ```
153
229
 
230
+ The generic-regex export is designed for direct consumption by any tool that supports regex matching -- Cisco AI Defense, Microsoft Agent Governance Toolkit, NemoClaw, or your custom pipeline.
231
+
154
232
  ---
155
233
 
156
234
  ## Contributing
@@ -169,6 +247,7 @@ Report what ATR found (or missed). **Your real-world detection report is more va
169
247
 
170
248
  | Impact | What to do | Time |
171
249
  |--------|-----------|------|
250
+ | **Critical** | **Integrate ATR into your security tool** -- PR our rules into your platform ([generic-regex export](#export-rules) makes it easy) | 1-2 hours |
172
251
  | **Critical** | Scan your MCP skills and [report results](https://github.com/Agent-Threat-Rule/agent-threat-rules/issues) | 15 min |
173
252
  | **Critical** | [Deploy ATR](docs/deployment-guide.md) in your agent pipeline, share detection stats | 1-2 hours |
174
253
  | **High** | [Break our rules](CONTRIBUTION-GUIDE.md#5-evasion-research) -- find bypasses, report evasions | 15 min |
@@ -178,6 +257,25 @@ Report what ATR found (or missed). **Your real-world detection report is more va
178
257
  | **Medium** | Add multilingual attack phrases for your native language | 30 min |
179
258
  | **Medium** | Run `npm run eval:pint` and share your results | 5 min |
180
259
 
260
+ ### For security platform maintainers
261
+
262
+ Want to integrate ATR into your product? Three options:
263
+
264
+ ```bash
265
+ # Option 1: Export rules as JSON (recommended for most tools)
266
+ atr convert generic-regex --output atr-rules.json
267
+ # → 100 rules, 685 regex patterns, severity/category metadata
268
+
269
+ # Option 2: Use the TypeScript engine directly
270
+ npm install agent-threat-rules
271
+ # → Full engine with evaluate() and scanSkill() APIs
272
+
273
+ # Option 3: GitHub Action for CI pipelines
274
+ # → One YAML line, SARIF output, GitHub Security tab integration
275
+ ```
276
+
277
+ Cisco AI Defense integrated via Option 1 ([PR #79](https://github.com/cisco-ai-defense/skill-scanner/pull/79)). Happy to help with your integration -- [open an issue](https://github.com/Agent-Threat-Rule/agent-threat-rules/issues) or email hello@panguard.ai.
278
+
181
279
  ### Rule contribution workflow
182
280
 
183
281
  ```
@@ -211,36 +309,47 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for the full guide. See [CONTRIBUTION-GUI
211
309
 
212
310
  ## Roadmap: From Format to Standard
213
311
 
214
- ```
215
- v0.2 (previous) v0.3 (current) v0.4+ (next)
216
- ┌─────────────────┐ ┌──────────────────┐ ┌──────────────────┐
217
- │ 61 rules │ → │ + Eval framework │ → │ 100+ rules │
218
- │ 2 engines (TS+Py)│ │ + PINT benchmark │ │ + Go engine │
219
- │ 2 SIEM converters│ │ + CI gate │ │ + ML classifier │
220
- │ 0 ext. benchmarks│ │ + Embedding (T2.5)│ │ + 10+ deployments│
221
- └─────────────────┘ │ + Honest numbers │ └──────────────────┘
222
- └──────────────────┘
223
- ```
224
-
225
312
  - [x] **v0.1** -- 44 rules, TypeScript engine, OWASP mapping
226
313
  - [x] **v0.2** -- MCP server, Layer 2-3 detection, pyATR, Splunk/Elastic converters
227
- - [x] **v0.3** -- Eval framework, PINT benchmark, CI gate, embedding similarity, honest numbers
228
- - [ ] **v0.4** -- Go engine, ML classifier integration, 100+ rules
229
- - [ ] **v1.0** -- Requires: 2+ engines, 10+ deployments, 100+ stable rules, schema review by 3+ security teams
314
+ - [x] **v0.3** -- Eval framework, PINT benchmark, CI gate, embedding similarity
315
+ - [x] **v0.4** -- 71 rules, ClawHub 36K scan, SAFE-MCP 91.8%
316
+ - [x] **v1.0** (current) -- 100 rules, 53K mega scan, GitHub Action + SARIF, generic-regex export, Cisco adoption
317
+ - [ ] **v1.1** -- Go engine, ML classifier integration, semantic signatures, community rule submissions
318
+ - [ ] **v2.0** -- Multi-engine standard: 2+ engines, 10+ production deployments, schema review by 3+ security teams
319
+
320
+ ### Strategic direction
321
+
322
+ | Phase | Goal | Status |
323
+ |-------|------|--------|
324
+ | **Phase 0: Core product** | 100 rules, 62.7% recall, OWASP 10/10, 53K scan | **Done** |
325
+ | **Phase 1: Distribution** | GitHub Action, SARIF, generic-regex export, ecosystem PRs | **Done** |
326
+ | **Phase 2: Adoption** | Cisco merged (34 rules), OWASP PR, 11 ecosystem PRs | **In progress** |
327
+ | **Phase 3: Community flywheel** | Threat Cloud crystallization, auto-generated rules, 10+ contributors | In progress |
328
+ | **Phase 4: Standard** | Multi-vendor adoption, OpenSSF submission, schema governance | Planned |
329
+
330
+ ATR uses "ATR Scanned" (not "ATR Certified") until recall exceeds 80%. We are honest about what we can and cannot detect. See [LIMITATIONS.md](LIMITATIONS.md).
230
331
 
231
332
  ---
232
333
 
233
334
  ## How It Works (Architecture)
234
335
 
235
336
  ```
236
- ATR (this repo) Your Product / Integration
237
- ┌────────────────────┐ ┌──────────────────────────┐
238
- │ Rules (61 YAML) match │ Block / Allow / Alert │
239
- │ Engine (TS + Py) ───────→ │ SIEM (Splunk / Elastic) │
240
- │ CLI / MCP / SIEM results │ Dashboard / Compliance
241
- │ │ Slack / PagerDuty / Email
242
- Detects threats Protects systems
243
- └────────────────────┘ └──────────────────────────┘
337
+ ATR (this repo) Your Product / Integration
338
+ ┌─────────────────────────┐ ┌──────────────────────────┐
339
+ 100 Rules (YAML) match │ Block / Allow / Alert │
340
+ │ Engine (TS + Py) ────────→ │ SIEM (Splunk / Elastic) │
341
+ │ CLI / MCP / GitHub Act. │ results │ CI/CD (SARIF → Security)
342
+ SARIF / Generic Regex │ │ Runtime Proxy (MCP)
343
+ Splunk / Elastic export Dashboard / Compliance
344
+ │ │ │ │
345
+ │ Detects threats │ │ Protects systems │
346
+ └─────────────────────────┘ └──────────────────────────┘
347
+
348
+ Integration paths:
349
+ 1. npm install → Use engine API directly
350
+ 2. GitHub Action → SARIF in Security tab
351
+ 3. atr convert → 685 patterns for any regex-capable tool
352
+ 4. MCP server → IDE integration (Claude, Cursor, etc.)
244
353
  ```
245
354
 
246
355
  See [INTEGRATION.md](INTEGRATION.md) for integration patterns. See [docs/deployment-guide.md](docs/deployment-guide.md) for step-by-step deployment instructions.
@@ -1 +1 @@
1
- {"version":3,"file":"badge.d.ts","sourceRoot":"","sources":["../src/badge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAQH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,aAAa,EAAE,CAAC,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;AAEtE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAiBD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,WAAW,GAAG,WAAW,CActE;AAMD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,CA+B5E;AAeD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,MAAM,CA+BpE;AAMD,wBAAgB,iBAAiB,CAC/B,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,GAClB,WAAW,GAAG,IAAI,CA6BpB;AAMD,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,MAAkE,GAC1E,MAAM,CAIR"}
1
+ {"version":3,"file":"badge.d.ts","sourceRoot":"","sources":["../src/badge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAQH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,aAAa,EAAE,CAAC,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;AAEtE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAiBD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,WAAW,GAAG,WAAW,CActE;AAMD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,CA+B5E;AAoBD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,MAAM,CA+BpE;AAMD,wBAAgB,iBAAiB,CAC/B,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,GAClB,WAAW,GAAG,IAAI,CA6BpB;AAMD,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,MAAkE,GAC1E,MAAM,CAIR"}
package/dist/badge.js CHANGED
@@ -80,7 +80,12 @@ export function generateBadgeEndpoint(summary) {
80
80
  // Generate standalone SVG badge
81
81
  // ---------------------------------------------------------------------------
82
82
  function escapeXml(str) {
83
- return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
83
+ return str
84
+ .replace(/&/g, '&amp;')
85
+ .replace(/</g, '&lt;')
86
+ .replace(/>/g, '&gt;')
87
+ .replace(/"/g, '&quot;')
88
+ .replace(/'/g, '&apos;');
84
89
  }
85
90
  function measureText(text) {
86
91
  // Approximate character width for Verdana 11px (shields.io standard)
package/dist/badge.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"badge.js","sourceRoot":"","sources":["../src/badge.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"badge.js","sourceRoot":"","sources":["../src/badge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Unified scan handler for ATR CLI.
3
+ * Auto-detects input type: JSON → MCP scan, .md → SKILL.md scan.
4
+ *
5
+ * @module agent-threat-rules/cli/scan-handler
6
+ */
7
+ import type { ScanType } from '../types.js';
8
+ export interface ScanOptions {
9
+ readonly rules?: string;
10
+ readonly json?: boolean;
11
+ readonly sarif?: boolean;
12
+ readonly severity?: string;
13
+ readonly forceType?: ScanType;
14
+ }
15
+ /** Detect whether the target is an MCP event JSON or SKILL.md file/directory. */
16
+ export declare function detectInputType(targetPath: string): ScanType;
17
+ /** Unified scan command: auto-detects MCP vs SKILL.md and runs the appropriate scan path. */
18
+ export declare function cmdScanUnified(target: string, rulesDir: string, options: ScanOptions): Promise<void>;
19
+ //# sourceMappingURL=scan-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-handler.d.ts","sourceRoot":"","sources":["../../src/cli/scan-handler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAoC,QAAQ,EAAE,MAAM,aAAa,CAAC;AAoB9E,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC;CAC/B;AAED,iFAAiF;AACjF,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,CA0B5D;AAED,6FAA6F;AAC7F,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAmBf"}
@@ -0,0 +1,257 @@
1
+ /**
2
+ * Unified scan handler for ATR CLI.
3
+ * Auto-detects input type: JSON → MCP scan, .md → SKILL.md scan.
4
+ *
5
+ * @module agent-threat-rules/cli/scan-handler
6
+ */
7
+ import { readFileSync, existsSync, statSync, readdirSync } from 'node:fs';
8
+ import { resolve } from 'node:path';
9
+ import { ATREngine } from '../engine.js';
10
+ import { scanResultToSARIF } from '../converters/sarif.js';
11
+ const SEVERITY_ORDER = ['informational', 'low', 'medium', 'high', 'critical'];
12
+ // ANSI colors
13
+ const RED = '\x1b[31m';
14
+ const GREEN = '\x1b[32m';
15
+ const DIM = '\x1b[2m';
16
+ const BOLD = '\x1b[1m';
17
+ const RESET = '\x1b[0m';
18
+ const SEVERITY_COLORS = {
19
+ critical: '\x1b[91m',
20
+ high: '\x1b[31m',
21
+ medium: '\x1b[33m',
22
+ low: '\x1b[36m',
23
+ informational: '\x1b[37m',
24
+ };
25
+ /** Detect whether the target is an MCP event JSON or SKILL.md file/directory. */
26
+ export function detectInputType(targetPath) {
27
+ if (targetPath.endsWith('.md'))
28
+ return 'skill';
29
+ if (targetPath.endsWith('.json'))
30
+ return 'mcp';
31
+ // Directory: inspect contents to decide
32
+ if (existsSync(targetPath) && statSync(targetPath).isDirectory()) {
33
+ const entries = readdirSync(targetPath);
34
+ const hasJson = entries.some((e) => e.endsWith('.json'));
35
+ const hasMd = entries.some((e) => e.endsWith('.md') || e.toLowerCase() === 'skill.md');
36
+ if (hasMd)
37
+ return 'skill';
38
+ if (hasJson)
39
+ return 'mcp';
40
+ return 'skill'; // default for empty or non-matching directories
41
+ }
42
+ // Attempt to detect by reading first bytes
43
+ if (existsSync(targetPath)) {
44
+ const head = readFileSync(targetPath, 'utf-8').slice(0, 100).trimStart();
45
+ if (head.startsWith('{') || head.startsWith('['))
46
+ return 'mcp';
47
+ if (head.startsWith('#') || head.startsWith('---'))
48
+ return 'skill';
49
+ }
50
+ throw new Error(`Cannot determine scan type for "${targetPath}". Use .json for MCP events or .md for SKILL.md files.`);
51
+ }
52
+ /** Unified scan command: auto-detects MCP vs SKILL.md and runs the appropriate scan path. */
53
+ export async function cmdScanUnified(target, rulesDir, options) {
54
+ if (!target) {
55
+ console.error(`${RED}Error: Missing target. Usage: atr scan <file|directory>${RESET}`);
56
+ process.exit(1);
57
+ }
58
+ const targetPath = resolve(target);
59
+ if (!existsSync(targetPath)) {
60
+ console.error(`${RED}Error: Path not found: ${targetPath}${RESET}`);
61
+ process.exit(1);
62
+ }
63
+ const scanType = options.forceType ?? detectInputType(targetPath);
64
+ if (scanType === 'skill') {
65
+ await scanSkillFiles(targetPath, rulesDir, options);
66
+ }
67
+ else {
68
+ await scanMcpEvents(targetPath, rulesDir, options);
69
+ }
70
+ }
71
+ // ── MCP Event Scan ─────────────────────────────────────────────
72
+ async function scanMcpEvents(eventsPath, rulesDir, options) {
73
+ const fileStat = statSync(eventsPath);
74
+ if (fileStat.size > 50 * 1024 * 1024) {
75
+ console.error(`${RED}Error: Events file exceeds 50MB limit${RESET}`);
76
+ process.exit(1);
77
+ }
78
+ const raw = readFileSync(eventsPath, 'utf-8');
79
+ let events;
80
+ try {
81
+ const parsed = JSON.parse(raw);
82
+ events = Array.isArray(parsed) ? parsed : [parsed];
83
+ }
84
+ catch {
85
+ console.error(`${RED}Error: Invalid JSON in ${eventsPath}${RESET}`);
86
+ process.exit(1);
87
+ }
88
+ const engine = new ATREngine({ rulesDir });
89
+ await engine.loadRules();
90
+ const minIdx = SEVERITY_ORDER.indexOf((options.severity ?? 'informational'));
91
+ const allResults = [];
92
+ let totalThreats = 0;
93
+ for (const event of events) {
94
+ if (!event.content)
95
+ continue; // skip malformed events
96
+ const result = engine.evaluateFull(event, eventsPath);
97
+ const filtered = result.matches.filter((m) => SEVERITY_ORDER.indexOf(m.rule.severity) >= minIdx);
98
+ if (filtered.length > 0) {
99
+ allResults.push({ event, result, filtered });
100
+ totalThreats += filtered.length;
101
+ }
102
+ }
103
+ if (options.sarif) {
104
+ const sarifResults = allResults.map(({ result, filtered }) => ({
105
+ ...result,
106
+ matches: filtered,
107
+ threat_count: filtered.length,
108
+ }));
109
+ const version = process.env['npm_package_version'] ?? '1.0.0';
110
+ console.log(JSON.stringify(scanResultToSARIF(sarifResults, version), null, 2));
111
+ return;
112
+ }
113
+ if (options.json) {
114
+ console.log(JSON.stringify({
115
+ scan_type: 'mcp',
116
+ events_scanned: events.length,
117
+ threats_detected: totalThreats,
118
+ rules_loaded: engine.getRuleCount(),
119
+ results: allResults.map(({ event, result, filtered }) => ({
120
+ content_hash: result.content_hash,
121
+ event: {
122
+ type: event.type,
123
+ timestamp: event.timestamp,
124
+ content_preview: event.content.slice(0, 100),
125
+ },
126
+ matches: filtered.map(formatMatchJson),
127
+ })),
128
+ }, null, 2));
129
+ return;
130
+ }
131
+ printScanHeader('MCP', events.length, engine.getRuleCount(), totalThreats);
132
+ if (totalThreats === 0) {
133
+ console.log(`${GREEN}No threats detected.${RESET}\n`);
134
+ return;
135
+ }
136
+ for (const { event, filtered } of allResults) {
137
+ const preview = event.content.slice(0, 80).replace(/\n/g, ' ');
138
+ console.log(` ${DIM}Event: [${event.type}] "${preview}..."${RESET}`);
139
+ for (const m of filtered) {
140
+ printMatch(m);
141
+ }
142
+ console.log('');
143
+ }
144
+ }
145
+ // ── SKILL.md Scan ──────────────────────────────────────────────
146
+ async function scanSkillFiles(targetPath, rulesDir, options) {
147
+ const skillFiles = collectSkillFiles(targetPath);
148
+ if (skillFiles.length === 0) {
149
+ console.error(`${RED}Error: No SKILL.md files found in ${targetPath}${RESET}`);
150
+ process.exit(1);
151
+ }
152
+ const engine = new ATREngine({ rulesDir });
153
+ await engine.loadRules();
154
+ const minIdx = SEVERITY_ORDER.indexOf((options.severity ?? 'informational'));
155
+ const allResults = [];
156
+ let totalThreats = 0;
157
+ for (const file of skillFiles) {
158
+ const fileSize = statSync(file).size;
159
+ if (fileSize > 1 * 1024 * 1024) {
160
+ console.error(`${RED}Warning: Skipping ${file} (${Math.round(fileSize / 1024)}KB exceeds 1MB limit)${RESET}`);
161
+ continue;
162
+ }
163
+ const content = readFileSync(file, 'utf-8');
164
+ const result = engine.scanSkillFull(content, file);
165
+ const filtered = result.matches.filter((m) => SEVERITY_ORDER.indexOf(m.rule.severity) >= minIdx);
166
+ if (filtered.length > 0) {
167
+ allResults.push({ file, result, filtered });
168
+ totalThreats += filtered.length;
169
+ }
170
+ }
171
+ if (options.sarif) {
172
+ const sarifResults = allResults.map(({ result, filtered }) => ({
173
+ ...result,
174
+ matches: filtered,
175
+ threat_count: filtered.length,
176
+ }));
177
+ const version = process.env['npm_package_version'] ?? '1.0.0';
178
+ console.log(JSON.stringify(scanResultToSARIF(sarifResults, version), null, 2));
179
+ return;
180
+ }
181
+ if (options.json) {
182
+ console.log(JSON.stringify({
183
+ scan_type: 'skill',
184
+ skills_scanned: skillFiles.length,
185
+ threats_detected: totalThreats,
186
+ rules_loaded: engine.getRuleCount(),
187
+ results: allResults.map(({ file, result, filtered }) => ({
188
+ file,
189
+ content_hash: result.content_hash,
190
+ matches: filtered.map(formatMatchJson),
191
+ })),
192
+ }, null, 2));
193
+ return;
194
+ }
195
+ printScanHeader('SKILL', skillFiles.length, engine.getRuleCount(), totalThreats);
196
+ if (totalThreats === 0) {
197
+ console.log(` ${GREEN}No threats detected.${RESET}\n`);
198
+ return;
199
+ }
200
+ for (const { file, filtered } of allResults) {
201
+ const relPath = file.replace(process.cwd() + '/', '');
202
+ console.log(` ${BOLD}${relPath}${RESET}`);
203
+ for (const m of filtered) {
204
+ printMatch(m);
205
+ }
206
+ console.log('');
207
+ }
208
+ }
209
+ // ── Shared Helpers ─────────────────────────────────────────────
210
+ function collectSkillFiles(targetPath) {
211
+ const files = [];
212
+ const stat = statSync(targetPath);
213
+ if (stat.isDirectory()) {
214
+ walkForSkills(targetPath, files);
215
+ }
216
+ else {
217
+ files.push(targetPath);
218
+ }
219
+ return files;
220
+ }
221
+ function walkForSkills(dir, out) {
222
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
223
+ const full = resolve(dir, entry.name);
224
+ if (entry.isDirectory()) {
225
+ walkForSkills(full, out);
226
+ }
227
+ else if (entry.name === 'SKILL.md' || entry.name === 'skill.md') {
228
+ out.push(full);
229
+ }
230
+ }
231
+ }
232
+ function formatMatchJson(m) {
233
+ return {
234
+ rule_id: m.rule.id,
235
+ title: m.rule.title,
236
+ severity: m.rule.severity,
237
+ confidence: m.confidence,
238
+ matched_conditions: m.matchedConditions,
239
+ };
240
+ }
241
+ function printScanHeader(type, scanned, rulesLoaded, threats) {
242
+ const label = type === 'MCP' ? 'Events' : 'Skills';
243
+ console.log(`\n${BOLD}ATR ${type} Scan Results${RESET}`);
244
+ console.log(`${DIM}${'─'.repeat(60)}${RESET}`);
245
+ console.log(` ${label} scanned: ${scanned}`);
246
+ console.log(` Rules loaded: ${rulesLoaded}`);
247
+ console.log(` Threats found: ${threats > 0 ? RED + threats + RESET : GREEN + '0' + RESET}`);
248
+ console.log(`${DIM}${'─'.repeat(60)}${RESET}`);
249
+ console.log(`${DIM} Open source (MIT). Star: https://github.com/Agent-Threat-Rule/agent-threat-rules${RESET}`);
250
+ console.log('');
251
+ }
252
+ function printMatch(m) {
253
+ const color = SEVERITY_COLORS[m.rule.severity] ?? '';
254
+ console.log(` ${color}${m.rule.severity.toUpperCase().padEnd(13)}${RESET} ${m.rule.id} - ${m.rule.title}`);
255
+ console.log(` ${DIM}Confidence: ${(m.confidence * 100).toFixed(0)}% | Conditions: ${m.matchedConditions.join(', ')}${RESET}`);
256
+ }
257
+ //# sourceMappingURL=scan-handler.js.map