agent-threat-rules 4.1.5 → 4.2.0
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.
- package/CHANGELOG.md +688 -1
- package/README.md +355 -125
- package/dist/adapters/openshell-filter.d.ts +10 -3
- package/dist/adapters/openshell-filter.d.ts.map +1 -1
- package/dist/adapters/openshell-filter.js +10 -3
- package/dist/adapters/openshell-filter.js.map +1 -1
- package/dist/adapters/stdio-adapter.js +3 -3
- package/dist/adapters/stdio-adapter.js.map +1 -1
- package/dist/cli/mcp-tools-document.d.ts +40 -0
- package/dist/cli/mcp-tools-document.d.ts.map +1 -0
- package/dist/cli/mcp-tools-document.js +95 -0
- package/dist/cli/mcp-tools-document.js.map +1 -0
- package/dist/cli/scan-handler.d.ts +8 -0
- package/dist/cli/scan-handler.d.ts.map +1 -1
- package/dist/cli/scan-handler.js +126 -34
- package/dist/cli/scan-handler.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +80 -43
- package/dist/cli.js.map +1 -1
- package/dist/converters/sarif.d.ts +14 -2
- package/dist/converters/sarif.d.ts.map +1 -1
- package/dist/converters/sarif.js +7 -9
- package/dist/converters/sarif.js.map +1 -1
- package/dist/corpus-event.d.ts +47 -5
- package/dist/corpus-event.d.ts.map +1 -1
- package/dist/corpus-event.js +68 -29
- package/dist/corpus-event.js.map +1 -1
- package/dist/embedding/model-loader.d.ts +2 -0
- package/dist/embedding/model-loader.d.ts.map +1 -1
- package/dist/embedding/model-loader.js +3 -1
- package/dist/embedding/model-loader.js.map +1 -1
- package/dist/enforcement.d.ts +8 -0
- package/dist/enforcement.d.ts.map +1 -1
- package/dist/enforcement.js +10 -0
- package/dist/enforcement.js.map +1 -1
- package/dist/engine.d.ts +150 -6
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +337 -76
- package/dist/engine.js.map +1 -1
- package/dist/eval/embedding-provenance.d.ts +40 -0
- package/dist/eval/embedding-provenance.d.ts.map +1 -0
- package/dist/eval/embedding-provenance.js +71 -0
- package/dist/eval/embedding-provenance.js.map +1 -0
- package/dist/eval/eval-harness.d.ts +34 -0
- package/dist/eval/eval-harness.d.ts.map +1 -1
- package/dist/eval/eval-harness.js +49 -2
- package/dist/eval/eval-harness.js.map +1 -1
- package/dist/eval/hackaprompt-corpus.d.ts +12 -0
- package/dist/eval/hackaprompt-corpus.d.ts.map +1 -1
- package/dist/eval/hackaprompt-corpus.js +15 -1
- package/dist/eval/hackaprompt-corpus.js.map +1 -1
- package/dist/eval/hackaprompt-provenance.d.ts +52 -0
- package/dist/eval/hackaprompt-provenance.d.ts.map +1 -0
- package/dist/eval/hackaprompt-provenance.js +108 -0
- package/dist/eval/hackaprompt-provenance.js.map +1 -0
- package/dist/eval/run-hackaprompt-benchmark.d.ts +18 -8
- package/dist/eval/run-hackaprompt-benchmark.d.ts.map +1 -1
- package/dist/eval/run-hackaprompt-benchmark.js +154 -64
- package/dist/eval/run-hackaprompt-benchmark.js.map +1 -1
- package/dist/eval/run-pint-benchmark.js +11 -1
- package/dist/eval/run-pint-benchmark.js.map +1 -1
- package/dist/eval/skill-benchmark.d.ts +26 -6
- package/dist/eval/skill-benchmark.d.ts.map +1 -1
- package/dist/eval/skill-benchmark.js +49 -16
- package/dist/eval/skill-benchmark.js.map +1 -1
- package/dist/flywheel.d.ts +4 -0
- package/dist/flywheel.d.ts.map +1 -1
- package/dist/flywheel.js +12 -4
- package/dist/flywheel.js.map +1 -1
- package/dist/hook-handler.d.ts +11 -1
- package/dist/hook-handler.d.ts.map +1 -1
- package/dist/hook-handler.js +84 -16
- package/dist/hook-handler.js.map +1 -1
- package/dist/hook-tool-output.d.ts +47 -0
- package/dist/hook-tool-output.d.ts.map +1 -0
- package/dist/hook-tool-output.js +110 -0
- package/dist/hook-tool-output.js.map +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/layer-integration.d.ts +4 -2
- package/dist/layer-integration.d.ts.map +1 -1
- package/dist/layer-integration.js +6 -2
- package/dist/layer-integration.js.map +1 -1
- package/dist/mcp-server.d.ts +8 -0
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +1 -0
- package/dist/mcp-server.js.map +1 -1
- package/dist/mcp-tools/scan.d.ts.map +1 -1
- package/dist/mcp-tools/scan.js +9 -1
- package/dist/mcp-tools/scan.js.map +1 -1
- package/dist/measurement/enforce-lane.d.ts +66 -0
- package/dist/measurement/enforce-lane.d.ts.map +1 -0
- package/dist/measurement/enforce-lane.js +89 -0
- package/dist/measurement/enforce-lane.js.map +1 -0
- package/dist/measurement/from-eval-harness.d.ts +16 -3
- package/dist/measurement/from-eval-harness.d.ts.map +1 -1
- package/dist/measurement/from-eval-harness.js +15 -5
- package/dist/measurement/from-eval-harness.js.map +1 -1
- package/dist/measurement/in-sample-ablation.d.ts +29 -0
- package/dist/measurement/in-sample-ablation.d.ts.map +1 -0
- package/dist/measurement/in-sample-ablation.js +45 -0
- package/dist/measurement/in-sample-ablation.js.map +1 -0
- package/dist/measurement/schema.d.ts +38 -1
- package/dist/measurement/schema.d.ts.map +1 -1
- package/dist/measurement/schema.js +43 -1
- package/dist/measurement/schema.js.map +1 -1
- package/dist/quality/action-eligibility.d.ts +4 -2
- package/dist/quality/action-eligibility.d.ts.map +1 -1
- package/dist/quality/action-eligibility.js.map +1 -1
- package/dist/rule-confidence.d.ts +31 -0
- package/dist/rule-confidence.d.ts.map +1 -0
- package/dist/rule-confidence.js +49 -0
- package/dist/rule-confidence.js.map +1 -0
- package/dist/rule-scaffolder.d.ts.map +1 -1
- package/dist/rule-scaffolder.js +43 -4
- package/dist/rule-scaffolder.js.map +1 -1
- package/dist/semantic-evaluator.d.ts.map +1 -1
- package/dist/semantic-evaluator.js +15 -13
- package/dist/semantic-evaluator.js.map +1 -1
- package/dist/shadow-evaluator.d.ts +7 -2
- package/dist/shadow-evaluator.d.ts.map +1 -1
- package/dist/shadow-evaluator.js +29 -25
- package/dist/shadow-evaluator.js.map +1 -1
- package/dist/testcase-event.d.ts +82 -1
- package/dist/testcase-event.d.ts.map +1 -1
- package/dist/testcase-event.js +135 -16
- package/dist/testcase-event.js.map +1 -1
- package/dist/tier0-invariant.d.ts.map +1 -1
- package/dist/tier0-invariant.js +1 -0
- package/dist/tier0-invariant.js.map +1 -1
- package/dist/tier1-blacklist.d.ts.map +1 -1
- package/dist/tier1-blacklist.js +1 -0
- package/dist/tier1-blacklist.js.map +1 -1
- package/dist/tool-definition-scope.d.ts +45 -0
- package/dist/tool-definition-scope.d.ts.map +1 -0
- package/dist/tool-definition-scope.js +68 -0
- package/dist/tool-definition-scope.js.map +1 -0
- package/dist/types.d.ts +42 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/verdict.d.ts +46 -2
- package/dist/verdict.d.ts.map +1 -1
- package/dist/verdict.js +99 -14
- package/dist/verdict.js.map +1 -1
- package/package.json +2 -1
- package/rules/agent-manipulation/ATR-2026-00030-cross-agent-attack.yaml +2 -2
- package/rules/agent-manipulation/ATR-2026-00139-casual-authority-redirect.yaml +1 -1
- package/rules/agent-manipulation/ATR-2026-00328-named-mode-persona-override.yaml +1 -1
- package/rules/agent-manipulation/ATR-2026-00330-yoja-allcaps-settings-block-persona.yaml +1 -1
- package/rules/agent-manipulation/ATR-2026-00418-weknora-mcp-config-rce.yaml +5 -5
- package/rules/agent-manipulation/ATR-2026-00432-superagi-output-handler-eval-rce.yaml +1 -1
- package/rules/agent-manipulation/ATR-2026-00440-semantic-kernel-vector-store-eval-rce.yaml +3 -3
- package/rules/context-exfiltration/ATR-2026-00426-nl-output-injection-credential-leak.yaml +1 -1
- package/rules/context-exfiltration/ATR-2026-00702-ipi-credential-exfil-via-agent-action.yaml +1 -1
- package/rules/context-exfiltration/ATR-2026-00707-ipi-file-exfil-with-destroy-or-tamper.yaml +1 -1
- package/rules/context-exfiltration/ATR-2026-00851-indirect-pi-financial-payment-exfil.yaml +1 -1
- package/rules/context-exfiltration/ATR-2026-01457-sysprompt-completion-clone-attack.yaml +1 -1
- package/rules/context-exfiltration/ATR-2026-01462-sysprompt-override-pseudo-admin.yaml +1 -1
- package/rules/context-exfiltration/ATR-2026-01463-imend-imsystem-prompt-injection-format.yaml +1 -1
- package/rules/context-exfiltration/ATR-2026-01606-ssrf-internal-network-scan.yaml +1 -1
- package/rules/context-exfiltration/ATR-2026-02847-policy-justified-exfil-to-email.yaml +1 -1
- package/rules/context-exfiltration/ATR-2026-02855-cross-email-exfil-relay.yaml +79 -0
- package/rules/context-exfiltration/ATR-2026-02861-exfil-send-email-with-body-directive.yaml +83 -0
- package/rules/context-exfiltration/ATR-2026-02866-idna-label-separator-in-fetch-url-host.yaml +104 -0
- package/rules/excessive-autonomy/ATR-2026-00710-ipi-physical-access-grant.yaml +1 -1
- package/rules/model-abuse/ATR-2026-01897-host-sabotage.yaml +2 -2
- package/rules/model-security/ATR-2026-00433-modelcache-torch-load-deserialization-rce.yaml +1 -1
- package/rules/privilege-escalation/ATR-2026-00436-enclave-vm-sandbox-escape-rce.yaml +1 -1
- package/rules/privilege-escalation/ATR-2026-01601-sql-injection-destructive-ddl.yaml +2 -2
- package/rules/privilege-escalation/ATR-2026-01612-debug-mode-privilege-escalation.yaml +1 -1
- package/rules/privilege-escalation/ATR-2026-02867-identifier-typed-tool-argument-dot-dot-path-segment.yaml +106 -0
- package/rules/prompt-injection/ATR-2026-00004-system-prompt-override.yaml +2 -2
- package/rules/prompt-injection/ATR-2026-00080-encoding-evasion.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-00086-visual-spoofing.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-00203-context-pollution-skill-description.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-00228-structured-jailbreak.yaml +2 -2
- package/rules/prompt-injection/ATR-2026-00233-structured-jailbreak.yaml +2 -2
- package/rules/prompt-injection/ATR-2026-00256-base-n-encoding-jailbreak.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-00264-latent-injection-translation.yaml +3 -3
- package/rules/prompt-injection/ATR-2026-00265-latent-injection-rag-document.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-00282-perez-prompt-injection-hijack.yaml +4 -4
- package/rules/prompt-injection/ATR-2026-00321-hyphenated-system-instruction-injection.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-00394-backspace-deletion-character-injection.yaml +2 -2
- package/rules/prompt-injection/ATR-2026-00446-variable-assignment-payload-injection.yaml +3 -3
- package/rules/prompt-injection/ATR-2026-00476-garak-web-injection-javascript-alert-1.yaml +3 -3
- package/rules/prompt-injection/ATR-2026-00481-garak-dan-mixedunassigned.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-00489-garak-encoding-javascript-alert-1.yaml +3 -3
- package/rules/prompt-injection/ATR-2026-00508-escape-delimiter-wrapped-goal-hijacking-in-user-input-prompt.yaml +3 -3
- package/rules/prompt-injection/ATR-2026-00512-rules-file-backdoor-injection.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-01005-many-shot-repetition-override.yaml +2 -2
- package/rules/prompt-injection/ATR-2026-01006-payload-split-string-concat-injection.yaml +2 -2
- package/rules/prompt-injection/ATR-2026-01015-evasion-refusal-suppression.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-01016-evasion-response-priming-forced-opener.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-01026-evasion-unicode-glitch-token-anomaly.yaml +2 -2
- package/rules/prompt-injection/ATR-2026-01304-indirect-injection-carrier-important-override.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-01759-dos-new-important-instructions-injection-in-document.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-01865-llmail-fake-email-boundary-marker.yaml +1 -1
- package/rules/prompt-injection/ATR-2026-02010-emoji-only-payload.yaml +2 -2
- package/rules/prompt-injection/ATR-2026-02815-injected-halt-order-canned-reply-dos.yaml +80 -0
- package/rules/prompt-injection/ATR-2026-02820-tool-result-orders-agent-to-hide-its-own-action.yaml +106 -0
- package/rules/prompt-injection/ATR-2026-02858-context-suppression-directive.yaml +73 -0
- package/rules/prompt-injection/ATR-2026-02859-tool-output-agent-role-reassignment.yaml +71 -0
- package/rules/prompt-injection/ATR-2026-02860-forged-instruction-boundary-marker.yaml +70 -0
- package/rules/prompt-injection/ATR-2026-02862-fake-chat-template-turn-injection.yaml +79 -0
- package/rules/skill-compromise/ATR-2026-00060-skill-impersonation.yaml +1 -1
- package/rules/skill-compromise/ATR-2026-00063-skill-chain-attack.yaml +2 -2
- package/rules/skill-compromise/ATR-2026-00066-parameter-injection.yaml +2 -2
- package/rules/skill-compromise/ATR-2026-00149-skill-exfil-compound.yaml +6 -6
- package/rules/skill-compromise/ATR-2026-00223-reverse-shell-dropper.yaml +2 -2
- package/rules/skill-compromise/ATR-2026-01755-backdoor-pot-linguistic-trigger-phrase.yaml +1 -1
- package/rules/skill-compromise/ATR-2026-01756-backdoor-pot-symbol-emoticon-trigger.yaml +1 -1
- package/rules/skill-compromise/ATR-2026-02261-code-credential-exfil-chain.yaml +1 -1
- package/rules/tool-poisoning/ATR-2026-00012-unauthorized-tool-call.yaml +23 -23
- package/rules/tool-poisoning/ATR-2026-00013-tool-ssrf.yaml +16 -5
- package/rules/tool-poisoning/ATR-2026-00161-important-tag-cross-tool-shadowing.yaml +1 -1
- package/rules/tool-poisoning/ATR-2026-00277-echo-template-command-injection.yaml +2 -2
- package/rules/tool-poisoning/ATR-2026-00494-garak-exploitation-mixedunassigned.yaml +4 -4
- package/rules/tool-poisoning/ATR-2026-00581-mcp-tool-rug-pull-post-approval-redefinition.yaml +1 -1
- package/rules/tool-poisoning/ATR-2026-01303-tool-schema-enumeration-social-engineering.yaml +1 -1
- package/rules/tool-poisoning/ATR-2026-01307-mcp-dns-rebinding-attack.yaml +2 -2
- package/rules/tool-poisoning/ATR-2026-01775-semantic-mcp-tool-manifest-poisoning.yaml +1 -1
- package/rules/tool-poisoning/ATR-2026-01932-shadow-undeclared-mcp-server-registration.yaml +1 -1
- package/rules/tool-poisoning/ATR-2026-02025-mcp-full-schema-poisoning-nondescription-field.yaml +1 -1
- package/rules/tool-poisoning/ATR-2026-02817-untrusted-content-dictates-tool-arguments.yaml +83 -0
- package/rules/tool-poisoning/ATR-2026-02864-non-canonical-ipv4-host-in-url-tool-argument.yaml +102 -0
- package/rules/tool-poisoning/ATR-2026-02865-read-only-utility-write-flag-in-command-argument.yaml +157 -0
- package/rules/tool-poisoning/ATR-2026-02868-ssrf-ipv6-transition-address-embedded-private-ipv4.yaml +107 -0
- package/spec/README.md +6 -3
- package/spec/atr-event-v1.0.md +13 -8
- package/spec/atr-method-v1.1.md +2 -2
- package/spec/category-registry/gemara/README.md +102 -0
- package/spec/category-registry/gemara/ai-agent-capability-catalog.yaml +157 -0
- package/spec/category-registry/gemara/atr-attack-vector-catalog.yaml +130 -0
- package/spec/category-registry/gemara/atr-detection-control-catalog.yaml +327 -0
- package/spec/category-registry/gemara/atr-threat-catalog.yaml +246 -0
- package/spec/category-registry/gemara/src/editorial.yaml +743 -0
- package/spec/mappings/README.md +1 -1
- package/spec/mappings/atr-to-nist-csf-2.0.md +5 -4
- package/spec/schema/event.schema.json +1 -1
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
title: Non-Canonical Numeric IPv4 Host in a URL-Typed Tool Argument (SSRF Filter Bypass)
|
|
2
|
+
id: ATR-2026-02864
|
|
3
|
+
rule_version: 1
|
|
4
|
+
status: experimental
|
|
5
|
+
description: |
|
|
6
|
+
Detects an agent or node tool call whose URL-typed argument names its host as a NON-CANONICAL numeric IPv4 literal: a bare decimal integer (http://2130706433/), a bare octal integer (http://017700000001/), a bare hex integer (http://0x7f000001/), a dotted address whose first part is octal or hex (http://0177.0.0.1/, http://0x7f.0.0.1/), or the two- and three-part short forms (http://127.1/, http://127.0.1/, http://10.1/). URL parsers and the OS resolver expand every one of these into an ordinary address such as 127.0.0.1, 10.0.0.1 or 169.254.169.254, but a deny-list that only recognises the four-part dotted-decimal spelling does not, so the request reaches loopback, LAN or cloud-metadata services the filter was meant to stop.
|
|
7
|
+
This is the bypass core of CVE-2026-101883 (OpenClaw Windows Node through 2026.9.4): the canvas.present capability skipped the URL risk evaluation that canvas.navigate enforced, and the regex-based check that did exist only matched dotted-decimal private ranges. Anyone with gateway or agent access could send canvas.present with {"url": "http://2130706433:8080/admin"} and make the node's WebView issue the request from the user's machine. The fix commit 16528aa added CanvasUrlSafety, whose tests enumerate exactly these encodings. The same parser differential applies to any URL-typed argument of any tool (fetch, browse, open_url, webhook targets), so the rule is not keyed to the canvas tool name.
|
|
8
|
+
SCOPE AND LIMITS, stated honestly. The rule matches only when the URL is a JSON string value with a quoted key ("url":"..."), a quoted element of a JSON array ({"urls":["..."]}), or the whole argument string, so prose that quotes the payload does not fire. A single trailing dot after the host (http://2130706433./) is accepted because WHATWG/Chromium URL parsers (the WebView2 used by the Windows node) drop the empty final label and still resolve it to 127.0.0.1. JSON-escaped slashes after the scheme (http:\/\/0x7f000001\/, as produced by PHP json_encode) are also accepted. It deliberately does NOT match canonical dotted-decimal loopback or private addresses (127.0.0.1, 10.x, 192.168.x), 0.0.0.0, the bare "0" host, or the 100.64.0.0/10 CGNAT / Tailscale range, even though the upstream fix blocks all of them: coding agents and node canvases legitimately open local dev servers, LAN devices and tailnet dashboards constantly, and those values are byte-for-byte the same shape as the attack. IPv4-mapped IPv6, [::1] and ULA ([fd00::1]) hosts are matched by ATR-2026-00013. That rule lists only a few hard-coded hex/octal loopback strings (0x7f000001, 0x7f.0x0.0x0.0x1, 017700000001 and similar), so the overlap is limited to those exact strings; general hex forms such as http://0x7f.1:9222/ are not matched by ATR-2026-00013 (its own evasion_tests list 0x7f.1 as a gap) and are covered here. Unicode full-width dots, percent-encoded hosts, zero-padded dotted-decimal octets (http://127.000.000.001/), scheme-relative URLs (//2130706433/) and DNS names that resolve to internal addresses are out of scope.
|
|
9
|
+
CWE-918 (Server-Side Request Forgery), CWE-1286 (Improper Validation of Syntactic Correctness of Input).
|
|
10
|
+
author: ATR Community (CVE sync)
|
|
11
|
+
date: 2026/10/22
|
|
12
|
+
schema_version: '0.1'
|
|
13
|
+
detection_tier: pattern
|
|
14
|
+
maturity: test
|
|
15
|
+
severity: high
|
|
16
|
+
references:
|
|
17
|
+
cve:
|
|
18
|
+
- CVE-2026-101883
|
|
19
|
+
ghsa:
|
|
20
|
+
- GHSA-7vch-pmw9-3g4q
|
|
21
|
+
euvd:
|
|
22
|
+
- EUVD-2026-90256
|
|
23
|
+
owasp_llm:
|
|
24
|
+
- LLM06:2025
|
|
25
|
+
owasp_agentic:
|
|
26
|
+
- ASI02:2026
|
|
27
|
+
mitre_atlas:
|
|
28
|
+
- AML.T0049 - Exploit Public-Facing Application
|
|
29
|
+
- AML.T0053 - AI Agent Tool Invocation
|
|
30
|
+
mitre_attack:
|
|
31
|
+
- T1090 - Proxy
|
|
32
|
+
- T1046 - Network Service Discovery
|
|
33
|
+
external:
|
|
34
|
+
- https://github.com/openclaw/openclaw-windows-node/security/advisories/GHSA-7vch-pmw9-3g4q
|
|
35
|
+
- https://github.com/openclaw/openclaw-windows-node/commit/16528aadaa45d7bc6718b07ccf8b01f3eb033ad1
|
|
36
|
+
- https://www.vulncheck.com/advisories/openclaw-windows-node-through-2026.9.4-ssrf-via-canvas-present
|
|
37
|
+
- https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-90256
|
|
38
|
+
metadata_provenance:
|
|
39
|
+
cve: human-reviewed
|
|
40
|
+
ghsa: human-reviewed
|
|
41
|
+
owasp_llm: human-reviewed
|
|
42
|
+
owasp_agentic: human-reviewed
|
|
43
|
+
mitre_atlas: human-reviewed
|
|
44
|
+
mitre_attack: human-reviewed
|
|
45
|
+
compliance:
|
|
46
|
+
eu_ai_act:
|
|
47
|
+
- article: '15'
|
|
48
|
+
context: Article 15 requires accuracy, robustness and cybersecurity appropriate to the intended purpose. A URL argument whose host is spelled as a decimal, octal or short-form IPv4 literal is a known way to slip past a dotted-decimal deny-list; this rule provides detection evidence supporting the Article 15 robustness obligation at the agent-to-tool boundary while the receiving tool remains unpatched.
|
|
49
|
+
strength: primary
|
|
50
|
+
- article: '9'
|
|
51
|
+
context: Article 9 risk management must account for the network reach an agent's tools give it. Runtime evidence that tool calls are steering a node's browser or fetcher at encoded internal addresses feeds the risk register for that surface.
|
|
52
|
+
strength: secondary
|
|
53
|
+
nist_ai_rmf:
|
|
54
|
+
- function: Measure
|
|
55
|
+
subcategory: MS.2.7
|
|
56
|
+
context: MS.2.7 requires AI system security and resilience to be evaluated and documented. Inspecting each URL-typed tool argument for non-canonical IPv4 host encodings is a measurable, per-invocation control against SSRF filter bypass rather than an assumption that every tool canonicalises hosts correctly.
|
|
57
|
+
strength: primary
|
|
58
|
+
- function: Manage
|
|
59
|
+
subcategory: MG.3.2
|
|
60
|
+
context: MG.3.2 requires pre-trained models and third-party components to be monitored. Node capabilities and MCP tools that fetch URLs are third-party components whose host validation the deployer cannot audit; flagging encoded internal hosts in their arguments is the monitoring control.
|
|
61
|
+
strength: secondary
|
|
62
|
+
iso_42001:
|
|
63
|
+
- clause: '8.1'
|
|
64
|
+
context: Clause 8.1 operational planning and control covers the tools an AI system invokes. This rule is an operational control applied to every URL-typed tool argument, independent of the tool's own URL validation.
|
|
65
|
+
strength: primary
|
|
66
|
+
- clause: '8.3'
|
|
67
|
+
context: Clause 8.3 AI risk treatment requires controls proportionate to identified risks. Because one encoded host can expose loopback admin panels or cloud metadata from the user's machine, inspecting URL hosts at call time is a proportionate treatment.
|
|
68
|
+
strength: secondary
|
|
69
|
+
tags:
|
|
70
|
+
category: tool-poisoning
|
|
71
|
+
subcategory: ssrf-encoded-ip-host
|
|
72
|
+
scan_target: tool_args
|
|
73
|
+
confidence: medium
|
|
74
|
+
source: cve-disclosure
|
|
75
|
+
vendor_sources: openclaw-windows-node-cve-2026-101883
|
|
76
|
+
agent_source:
|
|
77
|
+
type: tool_call
|
|
78
|
+
framework:
|
|
79
|
+
- any
|
|
80
|
+
provider:
|
|
81
|
+
- any
|
|
82
|
+
detection:
|
|
83
|
+
condition: any
|
|
84
|
+
method: pattern
|
|
85
|
+
conditions:
|
|
86
|
+
- field: tool_args
|
|
87
|
+
operator: regex
|
|
88
|
+
value: (?:^|\\?"[A-Za-z_][A-Za-z0-9_.-]{0,40}\\?"\s{0,3}:\s{0,3}\\?"|[\[,]\s{0,3}\\?")(?:https?|wss?|ftp):\\?/\\?/(?:[^/\s"\x27@?#\\]{1,64}@)?(?:\d{8,10}|0[0-7]{8,11}|0x[0-9a-f]{1,8}|0(?:[0-7]{1,3}|x[0-9a-f]{1,2})(?:\.(?:0x[0-9a-f]{1,2}|\d{1,4})){1,3}|\d{1,3}\.\d{1,8}|\d{1,3}\.\d{1,3}\.\d{1,5})\.?(?:[:/?#"\x27\\\s\]]|$)
|
|
89
|
+
description: A URL that is a quoted JSON value, a quoted JSON array element (or the whole argument) whose host is a decimal integer, octal integer, hex integer, octal/hex-first-part dotted address, or a two/three-part short IPv4 form - every spelling that resolves to an IPv4 address while evading a dotted-decimal deny-list
|
|
90
|
+
false_positives:
|
|
91
|
+
- Security test suites, SSRF fuzzing harnesses and CVE reproductions that deliberately pass encoded hosts in a URL argument will match; that is the intended behaviour for a detection corpus.
|
|
92
|
+
- A serialized tool call or JSON document quoted verbatim inside an article is the same bytes as the attack and still matches. Prose that mentions the URL without a quoted key does not.
|
|
93
|
+
- Embedded or industrial devices whose documentation tells users to reach them by a short-form address (http://10.1/) would match; this is rare and the short form is itself a canonicalisation hazard.
|
|
94
|
+
response:
|
|
95
|
+
actions:
|
|
96
|
+
- alert
|
|
97
|
+
- snapshot
|
|
98
|
+
actions_rationale: 'response-action eligibility (docs/RESPONSE-ACTION-ELIGIBILITY.md): new rule with no benign-corpus FP measurement beyond the gate; ceiling = observe.'
|
|
99
|
+
auto_response_threshold: high
|
|
100
|
+
message_template: |
|
|
101
|
+
[ATR-2026-02864] HIGH: a URL-typed tool argument names its host as a non-canonical numeric IPv4 literal (decimal / octal / hex integer, octal or hex dotted part, or short form). These spellings resolve to internal addresses such as 127.0.0.1 or 169.254.169.254 while slipping past dotted-decimal deny-lists - the CVE-2026-101883 canvas.present SSRF bypass. Canonicalise the host before applying URL policy.
|
|
102
|
+
confidence: 72
|
package/rules/tool-poisoning/ATR-2026-02865-read-only-utility-write-flag-in-command-argument.yaml
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
title: Read-Only Allow-Listed Utility Invoked With a File-Write or State-Changing Option in a Command Argument
|
|
2
|
+
id: ATR-2026-02865
|
|
3
|
+
rule_version: 1
|
|
4
|
+
status: experimental
|
|
5
|
+
description: |
|
|
6
|
+
Detects an agent or MCP tool call whose command-string argument starts with a utility that command allow-lists commonly classify as read-only (sort, uniq, diff, date, hostname, file) and gives it the option or operand that makes the utility itself write a file, run a helper program, or change system state. No shell metacharacter is involved: the allowed binary opens the destination on its own, so filters that only look for ; | && $( ) never see it.
|
|
7
|
+
This is the shape reported as CVE-2026-105789 / GHSA-85w2-wggf-rw49 against Microsoft UFO before 3.0.9. The execute_command tool of the Linux MCP server (ufo/client/mcp/http_servers/linux_mcp_server.py) accepted a free-form command string and allowed sort and uniq as read-only commands, so 'sort -o <file>' or the optional second uniq operand created or overwrote any file writable by the server process (agent configuration, shell startup files). The fix (commit 4f79362) removed sort and added per-command argument policies; its regression tests (tests/test_linux_mcp_command_policy.py) list the rejected forms used here. The command string is produced by the model, so a prompt injection that steers the next tool call is enough to reach it.
|
|
8
|
+
Six layers, each one utility's state-changing form: (1) sort with -o / an -o short-option cluster / --output (and its unique prefixes --o, --out ...), or --compress-program naming anything other than a real compressor (gzip, pigz, bzip2, xz, lz4, zstd, ...): sort executes that program, which turns a read-only allow-list entry into code execution; (2) diff with --output / --out / -o; (3) uniq with two non-option operands, where the second is the output file; option arguments of -f / -s / -w are consumed so 'uniq -f 2 data.txt' is not read as two operands; (4) date with -s / --set (and --se, --s) or a bare operand that is a valid MMDDhhmm[[CC]YY][.ss] timestamp (month 01-12, day 01-31, hour 00-23, minute 00-59), both of which set the system clock; a YYYYMMDD string such as 'date 20261022' is not a valid month and does not match; (5) hostname given a new name as its only operand, or -F / --file; (6) file with --compile (or its unique prefixes --co, --comp ...), which writes a compiled magic database (.mgc) into the working directory.
|
|
9
|
+
Scope and limits: the utility must be the FIRST token of a JSON string value whose key is command-like: command, cmd, cmd_line, command_line, cmdline, commandline, shell, script, exec, args, argv, optionally prefixed by a word ending in '_' or '-' (bash_command, shell_cmd) or by bash, shell, sh, run, exec, raw or full (shellCommand, runCmd). Keys that merely end in those letters (transcript, subscript) do not count. The utility may be prefixed by sudo or a /usr/bin, /bin or ./ path. A JSON-escaped tab (\t) between tokens is treated as whitespace, because the shell splits on it; an escaped newline is not, because it ends the command. Commands reached after a pipe, ';', '&&' or a newline, passed as an argv array, or wrapped by env / nice / timeout are out of scope (shell-metacharacter rules cover the chained forms). file -C cannot be told apart from the read-only file -c because the engine matches case-insensitively, so only the long --compile form is covered. file -z / --uncompress, which the same fix also rejects, is not covered: it runs only libmagic's fixed decompressor list, not an attacker-chosen program, and 'file -z archive.gz' is ordinary read-only use. uniq operands that are quoted are not matched. CWE-88, CWE-184.
|
|
10
|
+
RE2 portability: every condition compiles under RE2 (checked with Go regexp). The earlier lookarounds were replaced without changing behaviour: trailing lookaheads now consume the terminator, the uniq option/operand lookaheads became explicit alternatives, and the --compress-program compressor exception became a positive witness generated from the compressor/path trie. Measured equivalent to the lookaround version on 2.4M generated command strings and an exhaustive 1.48M-string sweep of the --compress-program value forms. The trade-off is size: Layer 1 is about 2.4 KB because a finite exception list has no short RE2 spelling.
|
|
11
|
+
Known false positive: in a general-purpose shell tool where writing files is allowed, the ordinary write forms of these utilities match exactly like the attack, for example 'sort -o sorted.txt data.txt', in-place sorting with 'sort -u -o names.txt names.txt', and 'uniq -c sorted.txt counts.txt'. The rule reports that a read-only-looking utility was used to write, not intent, which is why severity is medium and the response is observe-only.
|
|
12
|
+
author: ATR Community (CVE sync)
|
|
13
|
+
date: 2026/10/22
|
|
14
|
+
schema_version: '0.1'
|
|
15
|
+
detection_tier: pattern
|
|
16
|
+
maturity: test
|
|
17
|
+
severity: medium
|
|
18
|
+
references:
|
|
19
|
+
cve:
|
|
20
|
+
- CVE-2026-105789
|
|
21
|
+
ghsa:
|
|
22
|
+
- GHSA-85w2-wggf-rw49
|
|
23
|
+
cwe:
|
|
24
|
+
- CWE-88
|
|
25
|
+
- CWE-184
|
|
26
|
+
owasp_llm:
|
|
27
|
+
- LLM06:2025
|
|
28
|
+
- LLM03:2025
|
|
29
|
+
owasp_agentic:
|
|
30
|
+
- ASI02:2026
|
|
31
|
+
- ASI05:2026
|
|
32
|
+
- ASI04:2026
|
|
33
|
+
mitre_atlas:
|
|
34
|
+
- AML.T0053 - AI Agent Tool Invocation
|
|
35
|
+
mitre_attack:
|
|
36
|
+
- 'T1565.001 - Data Manipulation: Stored Data Manipulation'
|
|
37
|
+
- 'T1546.004 - Event Triggered Execution: Unix Shell Configuration Modification'
|
|
38
|
+
- T1202 - Indirect Command Execution
|
|
39
|
+
external:
|
|
40
|
+
- https://github.com/microsoft/UFO/security/advisories/GHSA-85w2-wggf-rw49
|
|
41
|
+
- https://github.com/microsoft/UFO/pull/349
|
|
42
|
+
- https://github.com/microsoft/UFO/commit/4f793622794f5d47810d54bed431c61f6a781dd6
|
|
43
|
+
- https://github.com/microsoft/UFO/releases/tag/v3.0.9
|
|
44
|
+
- https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-93406
|
|
45
|
+
metadata_provenance:
|
|
46
|
+
cve: euvd-sync
|
|
47
|
+
cwe: human-reviewed
|
|
48
|
+
owasp_llm: human-reviewed
|
|
49
|
+
owasp_agentic: human-reviewed
|
|
50
|
+
mitre_atlas: human-reviewed
|
|
51
|
+
mitre_attack: human-reviewed
|
|
52
|
+
compliance:
|
|
53
|
+
eu_ai_act:
|
|
54
|
+
- article: '15'
|
|
55
|
+
context: Article 15 requires cybersecurity appropriate to the intended purpose. An agent whose command tool trusts sort, uniq, diff, date, hostname or file as read-only can be steered into overwriting its own configuration or changing system state; flagging those utilities' write and set forms provides detection evidence supporting Article 15 robustness at the agent-to-OS boundary.
|
|
56
|
+
strength: primary
|
|
57
|
+
- article: '9'
|
|
58
|
+
context: Article 9 risk management must identify how an agent's tools can be misused. Matches from this rule provide detection evidence supporting Article 9 by showing when a command allow-list's read-only classification was exceeded through a utility's own options, a risk that first-token allow-lists do not capture.
|
|
59
|
+
strength: secondary
|
|
60
|
+
nist_ai_rmf:
|
|
61
|
+
- function: Measure
|
|
62
|
+
subcategory: MS.2.7
|
|
63
|
+
context: MS.2.7 requires security and resilience to be evaluated and documented. Inspecting each command-tool invocation for file-write and state-changing options of allow-listed utilities is a measurable per-call control rather than an assumption that the allow-list is complete.
|
|
64
|
+
strength: primary
|
|
65
|
+
- function: Manage
|
|
66
|
+
subcategory: MG.3.2
|
|
67
|
+
context: MG.3.2 covers monitoring of third-party components in operation. The vulnerable execute_command tool ships with an agent framework the deployer did not write; monitoring the commands it actually receives is the operational check on that component.
|
|
68
|
+
strength: secondary
|
|
69
|
+
iso_42001:
|
|
70
|
+
- clause: '8.1'
|
|
71
|
+
context: Clause 8.1 operational control over externally provided processes covers the MCP servers an agent invokes. This rule is applied to every command those servers receive, independent of the server's own argument policy.
|
|
72
|
+
strength: primary
|
|
73
|
+
- clause: '8.3'
|
|
74
|
+
context: Clause 8.3 AI risk treatment requires controls proportionate to the risk. Because one sort -o can overwrite the agent's own configuration, runtime detection of the write form is a proportionate treatment alongside upgrading the framework.
|
|
75
|
+
strength: secondary
|
|
76
|
+
tags:
|
|
77
|
+
category: tool-poisoning
|
|
78
|
+
subcategory: read-only-utility-write-option
|
|
79
|
+
scan_target: tool_args
|
|
80
|
+
confidence: medium
|
|
81
|
+
source: cve-disclosure
|
|
82
|
+
vendor_sources: microsoft-ufo-cve-2026-105789
|
|
83
|
+
agent_source:
|
|
84
|
+
type: mcp_exchange
|
|
85
|
+
framework:
|
|
86
|
+
- mcp
|
|
87
|
+
- any
|
|
88
|
+
provider:
|
|
89
|
+
- any
|
|
90
|
+
detection:
|
|
91
|
+
condition: any
|
|
92
|
+
method: pattern
|
|
93
|
+
false_positives:
|
|
94
|
+
- 'Known false positive: a general-purpose shell tool where the user genuinely asks the agent to write sorted or de-duplicated output to a file produces the same tool call as the attack, for example sort -o sorted.txt data.txt, in-place sort -u -o names.txt names.txt, or uniq -c sorted.txt counts.txt. The rule cannot tell intent; it reports that a utility commonly allow-listed as read-only was used in its write form.'
|
|
95
|
+
- Administrative agents that are meant to set the clock or hostname (date -s, hostname new-name) will match.
|
|
96
|
+
- Building a custom magic database with file -C -m magic --compile in a development shell will match.
|
|
97
|
+
- Security test suites and CVE reproductions that pass these command strings to a tool will match, which is intended for a detection corpus.
|
|
98
|
+
conditions:
|
|
99
|
+
- field: content
|
|
100
|
+
operator: regex
|
|
101
|
+
value: \\{0,7}"(?:\w{0,24}?[_-]|bash|shell|sh|run|exec|raw|full)?(?:command_line|commandline|cmd_line|cmdline|command|cmd|shell|script|exec|args?|argv)\\{0,7}"\s*:\s*\\{0,7}"\s{0,16}(?:sudo(?:\s|\\(?:\\\\?)?t){1,64})?(?:/(?:usr/)?(?:local/)?s?bin/|\./)?sort(?:(?:\s|\\(?:\\\\?)?t){1,64}[^\s"\\|;&<>]{1,80}){0,8}?(?:\s|\\(?:\\\\?)?t){1,64}(?:(?:-[bcdfghimnrsuvz]{0,8}o(?:\s|\\(?:\\\\?)?t){0,64}|--o(?:u(?:t(?:p(?:u(?:t)?)?)?)?)?(?:=|(?:\s|\\(?:\\\\?)?t){1,64}))(?:\\{1,7}"|')?[^\s"\\|;&<>{}\[\],-]|--co[a-z-]{0,16}(?:=|(?:\s|\\(?:\\\\?)?t){1,64})(?:'(?:[^/bglpxz]|$)|\\{1,7}"[^\s"\\|;&<>{}\[\],/bglpxz-]|[^\s"\\|;&<>{}\[\],'/bglpxz-]|(?:'|\\{1,7}")?(?:/(?:usr/)?(?:local/)?s?bin/(?:[^bglpxz]|$|b(?:[^rz]|$|r(?:[^o]|$|o(?:[^t]|$|t(?:[^l]|$|l(?:[^i]|$))))|z(?:[^i]|$|i(?:[^p]|$|p(?:[^2]|$))))|g(?:[^z]|$|z(?:[^i]|$|i(?:[^p]|$)))|l(?:[^bz]|$|b(?:[^z]|$|z(?:[^i]|$|i(?:[^p]|$|p(?:[^2]|$))))|z(?:[^4imo]|$|i(?:[^p]|$)|m(?:[^a]|$)|o(?:[^p]|$)))|p(?:[^bilxz]|$|b(?:[^z]|$|z(?:[^i]|$|i(?:[^p]|$|p(?:[^2]|$))))|i(?:[^g]|$|g(?:[^z]|$))|l(?:[^z]|$|z(?:[^i]|$|i(?:[^p]|$)))|x(?:[^z]|$)|z(?:[^s]|$|s(?:[^t]|$|t(?:[^d]|$))))|x(?:[^z]|$)|z(?:[^s]|$|s(?:[^t]|$|t(?:[^d]|$)))|(?:gzip|pigz|bzip2|pbzip2|lbzip2|xz|pxz|lz4|lzop|zstd|pzstd|lzma|lzip|plzip|brotli)(?:[^\s'"\\]|\\{8}|\\{1,7}(?:[^"\\t]|$)|\\{4,7}t))|/(?:[^blsu]|$|b(?:[^i]|$|i(?:[^n]|$|n(?:[^/]|$)))|l(?:[^o]|$|o(?:[^c]|$|c(?:[^a]|$|a(?:[^l]|$|l(?:[^/]|$|/(?:[^bs]|$|b(?:[^i]|$|i(?:[^n]|$|n(?:[^/]|$)))|s(?:[^b]|$|b(?:[^i]|$|i(?:[^n]|$|n(?:[^/]|$))))))))))|s(?:[^b]|$|b(?:[^i]|$|i(?:[^n]|$|n(?:[^/]|$))))|u(?:[^s]|$|s(?:[^r]|$|r(?:[^/]|$|/(?:[^bls]|$|b(?:[^i]|$|i(?:[^n]|$|n(?:[^/]|$)))|l(?:[^o]|$|o(?:[^c]|$|c(?:[^a]|$|a(?:[^l]|$|l(?:[^/]|$|/(?:[^bs]|$|b(?:[^i]|$|i(?:[^n]|$|n(?:[^/]|$)))|s(?:[^b]|$|b(?:[^i]|$|i(?:[^n]|$|n(?:[^/]|$))))))))))|s(?:[^b]|$|b(?:[^i]|$|i(?:[^n]|$|n(?:[^/]|$)))))))))|(?:b(?:[^rz]|$|r(?:[^o]|$|o(?:[^t]|$|t(?:[^l]|$|l(?:[^i]|$))))|z(?:[^i]|$|i(?:[^p]|$|p(?:[^2]|$))))|g(?:[^z]|$|z(?:[^i]|$|i(?:[^p]|$)))|l(?:[^bz]|$|b(?:[^z]|$|z(?:[^i]|$|i(?:[^p]|$|p(?:[^2]|$))))|z(?:[^4imo]|$|i(?:[^p]|$)|m(?:[^a]|$)|o(?:[^p]|$)))|p(?:[^bilxz]|$|b(?:[^z]|$|z(?:[^i]|$|i(?:[^p]|$|p(?:[^2]|$))))|i(?:[^g]|$|g(?:[^z]|$))|l(?:[^z]|$|z(?:[^i]|$|i(?:[^p]|$)))|x(?:[^z]|$)|z(?:[^s]|$|s(?:[^t]|$|t(?:[^d]|$))))|x(?:[^z]|$)|z(?:[^s]|$|s(?:[^t]|$|t(?:[^d]|$)))|(?:gzip|pigz|bzip2|pbzip2|lbzip2|xz|pxz|lz4|lzop|zstd|pzstd|lzma|lzip|plzip|brotli)(?:[^\s'"\\]|\\{8}|\\{1,7}(?:[^"\\t]|$)|\\{4,7}t)))))
|
|
102
|
+
description: sort invoked with an output file (-o / --output) or with --compress-program naming a non-compressor program - sort writes or executes it itself (CVE-2026-105789 primary primitive)
|
|
103
|
+
- field: tool_args
|
|
104
|
+
operator: regex
|
|
105
|
+
value: \\{0,7}"(?:\w{0,24}?[_-]|bash|shell|sh|run|exec|raw|full)?(?:command_line|commandline|cmd_line|cmdline|command|cmd|shell|script|exec|args?|argv)\\{0,7}"\s*:\s*\\{0,7}"\s{0,16}(?:sudo(?:\s|\\(?:\\\\?)?t){1,64})?(?:/(?:usr/)?(?:local/)?s?bin/|\./)?sort(?:(?:\s|\\(?:\\\\?)?t){1,64}[^\s"\\|;&<>]{1,80}){0,8}?(?:\s|\\(?:\\\\?)?t){1,64}(?:(?:-[bcdfghimnrsuvz]{0,8}o(?:\s|\\(?:\\\\?)?t){0,64}|--o(?:u(?:t(?:p(?:u(?:t)?)?)?)?)?(?:=|(?:\s|\\(?:\\\\?)?t){1,64}))(?:\\{1,7}"|')?[^\s"\\|;&<>{}\[\],-]|--co[a-z-]{0,16}(?:=|(?:\s|\\(?:\\\\?)?t){1,64})(?:'(?:[^/bglpxz]|$)|\\{1,7}"[^\s"\\|;&<>{}\[\],/bglpxz-]|[^\s"\\|;&<>{}\[\],'/bglpxz-]|(?:'|\\{1,7}")?(?:/(?:usr/)?(?:local/)?s?bin/(?:[^bglpxz]|$|b(?:[^rz]|$|r(?:[^o]|$|o(?:[^t]|$|t(?:[^l]|$|l(?:[^i]|$))))|z(?:[^i]|$|i(?:[^p]|$|p(?:[^2]|$))))|g(?:[^z]|$|z(?:[^i]|$|i(?:[^p]|$)))|l(?:[^bz]|$|b(?:[^z]|$|z(?:[^i]|$|i(?:[^p]|$|p(?:[^2]|$))))|z(?:[^4imo]|$|i(?:[^p]|$)|m(?:[^a]|$)|o(?:[^p]|$)))|p(?:[^bilxz]|$|b(?:[^z]|$|z(?:[^i]|$|i(?:[^p]|$|p(?:[^2]|$))))|i(?:[^g]|$|g(?:[^z]|$))|l(?:[^z]|$|z(?:[^i]|$|i(?:[^p]|$)))|x(?:[^z]|$)|z(?:[^s]|$|s(?:[^t]|$|t(?:[^d]|$))))|x(?:[^z]|$)|z(?:[^s]|$|s(?:[^t]|$|t(?:[^d]|$)))|(?:gzip|pigz|bzip2|pbzip2|lbzip2|xz|pxz|lz4|lzop|zstd|pzstd|lzma|lzip|plzip|brotli)(?:[^\s'"\\]|\\{8}|\\{1,7}(?:[^"\\t]|$)|\\{4,7}t))|/(?:[^blsu]|$|b(?:[^i]|$|i(?:[^n]|$|n(?:[^/]|$)))|l(?:[^o]|$|o(?:[^c]|$|c(?:[^a]|$|a(?:[^l]|$|l(?:[^/]|$|/(?:[^bs]|$|b(?:[^i]|$|i(?:[^n]|$|n(?:[^/]|$)))|s(?:[^b]|$|b(?:[^i]|$|i(?:[^n]|$|n(?:[^/]|$))))))))))|s(?:[^b]|$|b(?:[^i]|$|i(?:[^n]|$|n(?:[^/]|$))))|u(?:[^s]|$|s(?:[^r]|$|r(?:[^/]|$|/(?:[^bls]|$|b(?:[^i]|$|i(?:[^n]|$|n(?:[^/]|$)))|l(?:[^o]|$|o(?:[^c]|$|c(?:[^a]|$|a(?:[^l]|$|l(?:[^/]|$|/(?:[^bs]|$|b(?:[^i]|$|i(?:[^n]|$|n(?:[^/]|$)))|s(?:[^b]|$|b(?:[^i]|$|i(?:[^n]|$|n(?:[^/]|$))))))))))|s(?:[^b]|$|b(?:[^i]|$|i(?:[^n]|$|n(?:[^/]|$)))))))))|(?:b(?:[^rz]|$|r(?:[^o]|$|o(?:[^t]|$|t(?:[^l]|$|l(?:[^i]|$))))|z(?:[^i]|$|i(?:[^p]|$|p(?:[^2]|$))))|g(?:[^z]|$|z(?:[^i]|$|i(?:[^p]|$)))|l(?:[^bz]|$|b(?:[^z]|$|z(?:[^i]|$|i(?:[^p]|$|p(?:[^2]|$))))|z(?:[^4imo]|$|i(?:[^p]|$)|m(?:[^a]|$)|o(?:[^p]|$)))|p(?:[^bilxz]|$|b(?:[^z]|$|z(?:[^i]|$|i(?:[^p]|$|p(?:[^2]|$))))|i(?:[^g]|$|g(?:[^z]|$))|l(?:[^z]|$|z(?:[^i]|$|i(?:[^p]|$)))|x(?:[^z]|$)|z(?:[^s]|$|s(?:[^t]|$|t(?:[^d]|$))))|x(?:[^z]|$)|z(?:[^s]|$|s(?:[^t]|$|t(?:[^d]|$)))|(?:gzip|pigz|bzip2|pbzip2|lbzip2|xz|pxz|lz4|lzop|zstd|pzstd|lzma|lzip|plzip|brotli)(?:[^\s'"\\]|\\{8}|\\{1,7}(?:[^"\\t]|$)|\\{4,7}t)))))
|
|
106
|
+
description: Same as Layer 1, in the JSON-encoded tool argument object
|
|
107
|
+
- field: content
|
|
108
|
+
operator: regex
|
|
109
|
+
value: \\{0,7}"(?:\w{0,24}?[_-]|bash|shell|sh|run|exec|raw|full)?(?:command_line|commandline|cmd_line|cmdline|command|cmd|shell|script|exec|args?|argv)\\{0,7}"\s*:\s*\\{0,7}"\s{0,16}(?:sudo(?:\s|\\(?:\\\\?)?t){1,64})?(?:/(?:usr/)?(?:local/)?s?bin/|\./)?diff(?:(?:\s|\\(?:\\\\?)?t){1,64}[^\s"\\|;&<>]{1,80}){0,8}?(?:\s|\\(?:\\\\?)?t){1,64}(?:-[abcehnpqrtuvwyz]{0,6}o(?:\s|\\(?:\\\\?)?t){0,64}|--out(?:p(?:u(?:t)?)?)?(?:=|(?:\s|\\(?:\\\\?)?t){1,64}))(?:\\{1,7}"|')?[^\s"\\|;&<>{}\[\],-]
|
|
110
|
+
description: diff invoked with an output file (--output / --out / -o) - diff writes the destination itself
|
|
111
|
+
- field: tool_args
|
|
112
|
+
operator: regex
|
|
113
|
+
value: \\{0,7}"(?:\w{0,24}?[_-]|bash|shell|sh|run|exec|raw|full)?(?:command_line|commandline|cmd_line|cmdline|command|cmd|shell|script|exec|args?|argv)\\{0,7}"\s*:\s*\\{0,7}"\s{0,16}(?:sudo(?:\s|\\(?:\\\\?)?t){1,64})?(?:/(?:usr/)?(?:local/)?s?bin/|\./)?diff(?:(?:\s|\\(?:\\\\?)?t){1,64}[^\s"\\|;&<>]{1,80}){0,8}?(?:\s|\\(?:\\\\?)?t){1,64}(?:-[abcehnpqrtuvwyz]{0,6}o(?:\s|\\(?:\\\\?)?t){0,64}|--out(?:p(?:u(?:t)?)?)?(?:=|(?:\s|\\(?:\\\\?)?t){1,64}))(?:\\{1,7}"|')?[^\s"\\|;&<>{}\[\],-]
|
|
114
|
+
description: Same as Layer 2, in the JSON-encoded tool argument object
|
|
115
|
+
- field: content
|
|
116
|
+
operator: regex
|
|
117
|
+
value: \\{0,7}"(?:\w{0,24}?[_-]|bash|shell|sh|run|exec|raw|full)?(?:command_line|commandline|cmd_line|cmdline|command|cmd|shell|script|exec|args?|argv)\\{0,7}"\s*:\s*\\{0,7}"\s{0,16}(?:sudo(?:\s|\\(?:\\\\?)?t){1,64})?(?:/(?:usr/)?(?:local/)?s?bin/|\./)?uniq(?:(?:\s|\\(?:\\\\?)?t){1,64}[^\s"\\|;&<>{}\[\],-][^\s"\\|;&<>]{0,200}|(?:(?:\s|\\(?:\\\\?)?t){1,64}(?:--|-[a-z\d]{1,6}(?:(?:\s|\\(?:\\\\?)?t){1,64}\d{1,6})?|--[a-z][a-z-]{1,20}(?:=[\w-]{0,20}|(?:\s|\\(?:\\\\?)?t){1,64}\d{1,6})?)){0,5}(?:\s|\\(?:\\\\?)?t){1,64}(?:(?:-[a-z\d]{1,6}|--[a-z][a-z-]{1,20})(?:\s|\\(?:\\\\?)?t){1,64}[^\s"\\|;&<>{}\[\],\d-][^\s"\\|;&<>]{0,200}|(?:--|-[a-z\d]{1,6}(?:\s|\\(?:\\\\?)?t){1,64}\d{1,6}|--[a-z][a-z-]{1,20}(?:=[\w-]{0,20}|(?:\s|\\(?:\\\\?)?t){1,64}\d{1,6}))(?:\s|\\(?:\\\\?)?t){1,64}[^\s"\\|;&<>{}\[\],-][^\s"\\|;&<>]{0,200}))(?:\s|\\(?:\\\\?)?t){1,64}[^\s"\\|;&<>{}\[\],-][^\s"\\|;&<>]{0,200}(?:(?:\s|\\(?:\\\\?)?t)|\\{0,7}"|$)
|
|
118
|
+
description: uniq given an input and an output operand - uniq creates or overwrites the second file itself
|
|
119
|
+
- field: tool_args
|
|
120
|
+
operator: regex
|
|
121
|
+
value: \\{0,7}"(?:\w{0,24}?[_-]|bash|shell|sh|run|exec|raw|full)?(?:command_line|commandline|cmd_line|cmdline|command|cmd|shell|script|exec|args?|argv)\\{0,7}"\s*:\s*\\{0,7}"\s{0,16}(?:sudo(?:\s|\\(?:\\\\?)?t){1,64})?(?:/(?:usr/)?(?:local/)?s?bin/|\./)?uniq(?:(?:\s|\\(?:\\\\?)?t){1,64}[^\s"\\|;&<>{}\[\],-][^\s"\\|;&<>]{0,200}|(?:(?:\s|\\(?:\\\\?)?t){1,64}(?:--|-[a-z\d]{1,6}(?:(?:\s|\\(?:\\\\?)?t){1,64}\d{1,6})?|--[a-z][a-z-]{1,20}(?:=[\w-]{0,20}|(?:\s|\\(?:\\\\?)?t){1,64}\d{1,6})?)){0,5}(?:\s|\\(?:\\\\?)?t){1,64}(?:(?:-[a-z\d]{1,6}|--[a-z][a-z-]{1,20})(?:\s|\\(?:\\\\?)?t){1,64}[^\s"\\|;&<>{}\[\],\d-][^\s"\\|;&<>]{0,200}|(?:--|-[a-z\d]{1,6}(?:\s|\\(?:\\\\?)?t){1,64}\d{1,6}|--[a-z][a-z-]{1,20}(?:=[\w-]{0,20}|(?:\s|\\(?:\\\\?)?t){1,64}\d{1,6}))(?:\s|\\(?:\\\\?)?t){1,64}[^\s"\\|;&<>{}\[\],-][^\s"\\|;&<>]{0,200}))(?:\s|\\(?:\\\\?)?t){1,64}[^\s"\\|;&<>{}\[\],-][^\s"\\|;&<>]{0,200}(?:(?:\s|\\(?:\\\\?)?t)|\\{0,7}"|$)
|
|
122
|
+
description: Same as Layer 3, in the JSON-encoded tool argument object
|
|
123
|
+
- field: content
|
|
124
|
+
operator: regex
|
|
125
|
+
value: \\{0,7}"(?:\w{0,24}?[_-]|bash|shell|sh|run|exec|raw|full)?(?:command_line|commandline|cmd_line|cmdline|command|cmd|shell|script|exec|args?|argv)\\{0,7}"\s*:\s*\\{0,7}"\s{0,16}(?:sudo(?:\s|\\(?:\\\\?)?t){1,64})?(?:/(?:usr/)?(?:local/)?s?bin/|\./)?date(?:(?:(?:\s|\\(?:\\\\?)?t){1,64}[^\s"\\|;&<>]{1,80}){0,8}?(?:\s|\\(?:\\\\?)?t){1,64}(?:-u?s(?:\s|\\(?:\\\\?)?t){0,64}|--s(?:et?)?(?:=|(?:\s|\\(?:\\\\?)?t){1,64}))(?:\\{1,7}"|')?[^\s"\\|;&<>{}\[\],-]|(?:(?:\s|\\(?:\\\\?)?t){1,64}(?:-u|--utc|--universal)){0,3}(?:\s|\\(?:\\\\?)?t){1,64}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])(?:[01]\d|2[0-3])[0-5]\d(?:\d{2}){0,2}(?:\.\d{2})?(?:(?:\s|\\(?:\\\\?)?t)|\\{0,7}"|$))
|
|
126
|
+
description: date invoked with -s / --set or a valid MMDDhhmm operand - both set the system clock
|
|
127
|
+
- field: tool_args
|
|
128
|
+
operator: regex
|
|
129
|
+
value: \\{0,7}"(?:\w{0,24}?[_-]|bash|shell|sh|run|exec|raw|full)?(?:command_line|commandline|cmd_line|cmdline|command|cmd|shell|script|exec|args?|argv)\\{0,7}"\s*:\s*\\{0,7}"\s{0,16}(?:sudo(?:\s|\\(?:\\\\?)?t){1,64})?(?:/(?:usr/)?(?:local/)?s?bin/|\./)?date(?:(?:(?:\s|\\(?:\\\\?)?t){1,64}[^\s"\\|;&<>]{1,80}){0,8}?(?:\s|\\(?:\\\\?)?t){1,64}(?:-u?s(?:\s|\\(?:\\\\?)?t){0,64}|--s(?:et?)?(?:=|(?:\s|\\(?:\\\\?)?t){1,64}))(?:\\{1,7}"|')?[^\s"\\|;&<>{}\[\],-]|(?:(?:\s|\\(?:\\\\?)?t){1,64}(?:-u|--utc|--universal)){0,3}(?:\s|\\(?:\\\\?)?t){1,64}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])(?:[01]\d|2[0-3])[0-5]\d(?:\d{2}){0,2}(?:\.\d{2})?(?:(?:\s|\\(?:\\\\?)?t)|\\{0,7}"|$))
|
|
130
|
+
description: Same as Layer 4, in the JSON-encoded tool argument object
|
|
131
|
+
- field: content
|
|
132
|
+
operator: regex
|
|
133
|
+
value: \\{0,7}"(?:\w{0,24}?[_-]|bash|shell|sh|run|exec|raw|full)?(?:command_line|commandline|cmd_line|cmdline|command|cmd|shell|script|exec|args?|argv)\\{0,7}"\s*:\s*\\{0,7}"\s{0,16}(?:sudo(?:\s|\\(?:\\\\?)?t){1,64})?(?:/(?:usr/)?(?:local/)?s?bin/|\./)?hostname(?:(?:\s|\\(?:\\\\?)?t){1,64}(?:-[bv]{1,2}|--boot|--verbose)){0,3}(?:\s|\\(?:\\\\?)?t){1,64}(?:(?:-[bv]{0,2}f(?:\s|\\(?:\\\\?)?t){0,64}|--fi(?:le?)?(?:=|(?:\s|\\(?:\\\\?)?t){1,64}))(?:\\{1,7}"|')?[^\s"\\|;&<>{}\[\],-]|[a-z0-9][a-z0-9.-]{0,252}(?:\s|\\(?:\\\\?)?t){0,16}(?:\\{0,7}"|$))
|
|
134
|
+
description: hostname given a new name or a name file - hostname changes the system hostname
|
|
135
|
+
- field: tool_args
|
|
136
|
+
operator: regex
|
|
137
|
+
value: \\{0,7}"(?:\w{0,24}?[_-]|bash|shell|sh|run|exec|raw|full)?(?:command_line|commandline|cmd_line|cmdline|command|cmd|shell|script|exec|args?|argv)\\{0,7}"\s*:\s*\\{0,7}"\s{0,16}(?:sudo(?:\s|\\(?:\\\\?)?t){1,64})?(?:/(?:usr/)?(?:local/)?s?bin/|\./)?hostname(?:(?:\s|\\(?:\\\\?)?t){1,64}(?:-[bv]{1,2}|--boot|--verbose)){0,3}(?:\s|\\(?:\\\\?)?t){1,64}(?:(?:-[bv]{0,2}f(?:\s|\\(?:\\\\?)?t){0,64}|--fi(?:le?)?(?:=|(?:\s|\\(?:\\\\?)?t){1,64}))(?:\\{1,7}"|')?[^\s"\\|;&<>{}\[\],-]|[a-z0-9][a-z0-9.-]{0,252}(?:\s|\\(?:\\\\?)?t){0,16}(?:\\{0,7}"|$))
|
|
138
|
+
description: Same as Layer 5, in the JSON-encoded tool argument object
|
|
139
|
+
- field: content
|
|
140
|
+
operator: regex
|
|
141
|
+
value: \\{0,7}"(?:\w{0,24}?[_-]|bash|shell|sh|run|exec|raw|full)?(?:command_line|commandline|cmd_line|cmdline|command|cmd|shell|script|exec|args?|argv)\\{0,7}"\s*:\s*\\{0,7}"\s{0,16}(?:sudo(?:\s|\\(?:\\\\?)?t){1,64})?(?:/(?:usr/)?(?:local/)?s?bin/|\./)?file(?:(?:\s|\\(?:\\\\?)?t){1,64}[^\s"\\|;&<>]{1,80}){0,8}?(?:\s|\\(?:\\\\?)?t){1,64}--co(?:m(?:p(?:i(?:l(?:e)?)?)?)?)?(?:(?:\s|\\(?:\\\\?)?t)|\\{0,7}"|$)
|
|
142
|
+
description: file invoked with --compile - file writes a compiled magic database into the working directory
|
|
143
|
+
- field: tool_args
|
|
144
|
+
operator: regex
|
|
145
|
+
value: \\{0,7}"(?:\w{0,24}?[_-]|bash|shell|sh|run|exec|raw|full)?(?:command_line|commandline|cmd_line|cmdline|command|cmd|shell|script|exec|args?|argv)\\{0,7}"\s*:\s*\\{0,7}"\s{0,16}(?:sudo(?:\s|\\(?:\\\\?)?t){1,64})?(?:/(?:usr/)?(?:local/)?s?bin/|\./)?file(?:(?:\s|\\(?:\\\\?)?t){1,64}[^\s"\\|;&<>]{1,80}){0,8}?(?:\s|\\(?:\\\\?)?t){1,64}--co(?:m(?:p(?:i(?:l(?:e)?)?)?)?)?(?:(?:\s|\\(?:\\\\?)?t)|\\{0,7}"|$)
|
|
146
|
+
description: Same as Layer 6, in the JSON-encoded tool argument object
|
|
147
|
+
response:
|
|
148
|
+
actions:
|
|
149
|
+
- alert
|
|
150
|
+
- snapshot
|
|
151
|
+
actions_rationale: 'response-action eligibility (docs/RESPONSE-ACTION-ELIGIBILITY.md): new rule with no FP measurement on the 65K benign corpus and a documented false positive on ordinary write forms in general-purpose shell tools; ceiling = observe. No block action.'
|
|
152
|
+
auto_response_threshold: high
|
|
153
|
+
notify:
|
|
154
|
+
- security_team
|
|
155
|
+
message_template: |
|
|
156
|
+
[ATR-2026-02865] MEDIUM: a command tool argument uses a utility that command allow-lists commonly treat as read-only (sort, uniq, diff, date, hostname, file) in a form that writes a file, runs a helper program or changes system state. This is the CVE-2026-105789 (Microsoft UFO execute_command) pattern. In a general-purpose shell tool this can be an ordinary write; check whether the tool is meant to be read-only and enforces a per-command argument policy, and upgrade UFO to 3.0.9 or later.
|
|
157
|
+
confidence: 60
|
package/rules/tool-poisoning/ATR-2026-02868-ssrf-ipv6-transition-address-embedded-private-ipv4.yaml
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
title: SSRF via IPv6 Transition Address Embedding a Private IPv4 Target
|
|
2
|
+
id: ATR-2026-02868
|
|
3
|
+
rule_version: 1
|
|
4
|
+
status: experimental
|
|
5
|
+
description: |
|
|
6
|
+
Detects an agent tool argument whose URL host (or host-typed argument) is an IPv6 transition-mechanism address that carries an internal IPv4 address in its embedded 32 bits: IPv4-compatible (::a.b.c.d / ::XXXX:XXXX), IPv4-mapped written in hex (::ffff:XXXX:XXXX), IPv4-translated (::ffff:0:XXXX:XXXX), 6to4 (2002:XXXX:XXXX::/48) and NAT64 (64:ff9b::/96 and the local-use 64:ff9b:1::/48 prefix written with the IPv4 in the last 32 bits). The embedded IPv4 must fall in loopback 127.0.0.0/8, RFC 1918 (10/8, 172.16/12, 192.168/16) or link-local / cloud metadata 169.254/16.
|
|
7
|
+
Source: CVE-2026-55787 / GHSA-794r-5rp2-fpg8 in flytohub/flyto-core (< 2.26.3), an execution kernel for AI-agent workflows. Its validate_url_ssrf / is_private_ip guard compared resolved addresses only against native private IPv4 and IPv6 ranges and never unwrapped transition addresses, so a workflow author could hand http.get, http.request, browser.goto or llm.chat a URL such as http://[64:ff9b::a9fe:a9fe]/ and reach cloud metadata, loopback or RFC 1918 services. The same blind spot exists in any agent fetch tool whose guard checks ipaddress.is_private on the IPv6 object without first extracting the embedded IPv4.
|
|
8
|
+
Scope and limits: the rule matches the address literal only when it is the bracketed host of a URL (scheme://[...] or the %5B/%5D percent-encoded bracket form) or the value of a host / hostname / ip / address argument. It does not resolve DNS names that point at transition addresses, does not cover 100.64.0.0/10 CGNAT or 0.0.0.0/8 embeddings, and does not decode the RFC 6052 /32-/64 NAT64 layouts where the IPv4 is split around the u-octet. Overlap is intentional and partial: the dotted IPv4-mapped forms are already covered by ATR-2026-00013, hex ::ffff:7f00:1 / ::ffff:a9fe:a9fe by ATR-2026-02193, and the 169.254.169.254 NAT64 / 6to4 spellings by ATR-2026-02140; this rule adds the IPv4-compatible hex, 6to4 and NAT64 embeddings of loopback and RFC 1918 targets that no existing rule covers. CWE-918 (Server-Side Request Forgery).
|
|
9
|
+
author: ATR Community (CVE sync)
|
|
10
|
+
date: 2026/10/22
|
|
11
|
+
schema_version: '0.1'
|
|
12
|
+
detection_tier: pattern
|
|
13
|
+
maturity: test
|
|
14
|
+
severity: high
|
|
15
|
+
references:
|
|
16
|
+
cve:
|
|
17
|
+
- CVE-2026-55787
|
|
18
|
+
ghsa:
|
|
19
|
+
- GHSA-794r-5rp2-fpg8
|
|
20
|
+
euvd:
|
|
21
|
+
- EUVD-2026-101588
|
|
22
|
+
cwe:
|
|
23
|
+
- CWE-918
|
|
24
|
+
owasp_llm:
|
|
25
|
+
- LLM06:2025
|
|
26
|
+
owasp_agentic:
|
|
27
|
+
- ASI02:2026
|
|
28
|
+
mitre_atlas:
|
|
29
|
+
- AML.T0049 - Exploit Public-Facing Application
|
|
30
|
+
mitre_attack:
|
|
31
|
+
- 'T1552.005 - Unsecured Credentials: Cloud Instance Metadata API'
|
|
32
|
+
- T1046 - Network Service Discovery
|
|
33
|
+
external:
|
|
34
|
+
- https://github.com/flytohub/flyto-core/security/advisories/GHSA-794r-5rp2-fpg8
|
|
35
|
+
- https://github.com/flytohub/flyto-core/commit/519e040384a74984848c4e84a6600e5f1020a6d2
|
|
36
|
+
- https://github.com/flytohub/flyto-core/releases/tag/v2.26.3
|
|
37
|
+
- https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-101588
|
|
38
|
+
metadata_provenance:
|
|
39
|
+
cve: human-reviewed
|
|
40
|
+
ghsa: human-reviewed
|
|
41
|
+
cwe: human-reviewed
|
|
42
|
+
owasp_llm: human-reviewed
|
|
43
|
+
owasp_agentic: human-reviewed
|
|
44
|
+
mitre_atlas: human-reviewed
|
|
45
|
+
mitre_attack: human-reviewed
|
|
46
|
+
compliance:
|
|
47
|
+
eu_ai_act:
|
|
48
|
+
- article: '15'
|
|
49
|
+
context: Article 15 (accuracy, robustness and cybersecurity) requires high-risk AI systems to resist attempts to misuse their tools; this rule provides detection evidence supporting that requirement by flagging agent fetch-tool URLs that smuggle an internal IPv4 target inside an IPv6 transition address past an SSRF guard.
|
|
50
|
+
strength: primary
|
|
51
|
+
- article: '9'
|
|
52
|
+
context: Article 9 risk management must treat identified risks; this rule provides detection evidence supporting the treatment of the transition-address SSRF bypass risk for agent workflows whose outbound-request guard may not unwrap embedded IPv4.
|
|
53
|
+
strength: secondary
|
|
54
|
+
nist_ai_rmf:
|
|
55
|
+
- function: Measure
|
|
56
|
+
subcategory: MS.2.7
|
|
57
|
+
context: MS.2.7 requires AI system security and resilience to be evaluated and documented; inspecting every outbound URL argument for transition-encoded internal targets is a measurable per-invocation control at the agent-to-network boundary.
|
|
58
|
+
strength: primary
|
|
59
|
+
- function: Manage
|
|
60
|
+
subcategory: MG.3.2
|
|
61
|
+
context: MG.3.2 requires third-party and pre-built components to be monitored in operation; agent workflow kernels such as flyto-core ship their own SSRF guards, and this rule monitors for the input class those guards are known to miss.
|
|
62
|
+
strength: secondary
|
|
63
|
+
iso_42001:
|
|
64
|
+
- clause: '8.1'
|
|
65
|
+
context: Clause 8.1 operational planning and control is supported by this rule's runtime detection of transition-address SSRF targets in agent tool calls.
|
|
66
|
+
strength: primary
|
|
67
|
+
- clause: '8.3'
|
|
68
|
+
context: Clause 8.3 AI risk treatment is supported by this rule's detection of the SSRF guard bypass before the request leaves the agent.
|
|
69
|
+
strength: secondary
|
|
70
|
+
tags:
|
|
71
|
+
category: tool-poisoning
|
|
72
|
+
subcategory: ssrf-ipv6-transition-address-bypass
|
|
73
|
+
scan_target: tool_args
|
|
74
|
+
confidence: high
|
|
75
|
+
source: cve-disclosure
|
|
76
|
+
agent_source:
|
|
77
|
+
type: tool_call
|
|
78
|
+
framework:
|
|
79
|
+
- any
|
|
80
|
+
provider:
|
|
81
|
+
- any
|
|
82
|
+
detection:
|
|
83
|
+
condition: any
|
|
84
|
+
false_positives:
|
|
85
|
+
- Security test suites and SSRF-guard fixtures (for example flyto-core tests/core/test_ssrf_ipv6_transition.py) that pass these addresses to a fetch tool on purpose will match; that is the intended behaviour for a detection corpus.
|
|
86
|
+
- A NAT64 or 6to4 deployment that deliberately reaches an internal RFC 1918 service through its translated IPv6 address would match; such deployments are rare and should be allow-listed by destination.
|
|
87
|
+
- Public IPv4 embeddings (::ffff:8.8.8.8, 64:ff9b::808:808, 2002:808:808::) and ordinary native IPv6 hosts do not match.
|
|
88
|
+
conditions:
|
|
89
|
+
- field: tool_args
|
|
90
|
+
operator: regex
|
|
91
|
+
value: (?i)//(?:[^\s/@\[\]"]{1,64}@)?(?:\[|%5b)(?:(?:(?:0{1,4}:){1,4}|:):(?:ffff:(?:0{1,4}:)?)?|(?:0{1,4}:){5}(?:ffff|0{1,4}):|0{0,2}64:ff9b(?::0{0,3}1)?(?::0{1,4}){0,4}::?)(?:(?:7f[0-9a-f]{2}|0?a[0-9a-f]{2}|ac1[0-9a-f]|c0a8|a9fe):[0-9a-f]{1,4}|(?:127|10)(?:\.\d{1,3}){3}|172\.(?:1[6-9]|2\d|3[01])(?:\.\d{1,3}){2}|(?:192\.168|169\.254)(?:\.\d{1,3}){2})(?:\]|%5d)
|
|
92
|
+
description: URL bracketed host is an IPv4-compatible, IPv4-mapped/translated or NAT64 address whose final 32 bits are a loopback, RFC 1918 or link-local IPv4
|
|
93
|
+
- field: tool_args
|
|
94
|
+
operator: regex
|
|
95
|
+
value: (?i)//(?:[^\s/@\[\]"]{1,64}@)?(?:\[|%5b)0{0,2}2002:(?:7f[0-9a-f]{2}|0?a[0-9a-f]{2}|ac1[0-9a-f]|c0a8|a9fe):[0-9a-f:]{0,35}(?:\]|%5d)
|
|
96
|
+
description: URL bracketed host is a 6to4 (2002::/16) address whose embedded IPv4 (bits 16-47) is a loopback, RFC 1918 or link-local IPv4
|
|
97
|
+
- field: tool_args
|
|
98
|
+
operator: regex
|
|
99
|
+
value: (?i)["'](?:host|hostname|host_?name|ip|ip_?addr(?:ess)?|addr(?:ess)?|target_?host|server)["']\s*:\s*["']\[?(?:(?:(?:0{1,4}:){1,4}|:):(?:ffff:(?:0{1,4}:)?)?(?:7f[0-9a-f]{2}|0?a[0-9a-f]{2}|ac1[0-9a-f]|c0a8|a9fe):[0-9a-f]{1,4}|0{0,2}64:ff9b(?::0{0,3}1)?(?::0{1,4}){0,4}::?(?:7f[0-9a-f]{2}|0?a[0-9a-f]{2}|ac1[0-9a-f]|c0a8|a9fe):[0-9a-f]{1,4}|0{0,2}2002:(?:7f[0-9a-f]{2}|0?a[0-9a-f]{2}|ac1[0-9a-f]|c0a8|a9fe):[0-9a-f:]{0,35})\]?["']
|
|
100
|
+
description: Host-typed tool argument whose whole value is an IPv6 transition address (hex embedding) carrying a loopback, RFC 1918 or link-local IPv4
|
|
101
|
+
response:
|
|
102
|
+
actions:
|
|
103
|
+
- alert
|
|
104
|
+
- snapshot
|
|
105
|
+
message_template: |
|
|
106
|
+
[ATR-2026-02868] HIGH: an agent tool is being sent to an IPv6 transition address (IPv4-compatible, IPv4-mapped, 6to4 or NAT64) whose embedded IPv4 is loopback, private or cloud metadata. This is the CVE-2026-55787 (flyto-core) SSRF guard bypass shape: unwrap the embedded IPv4 before the private-address check, and block the request.
|
|
107
|
+
confidence: 78
|
package/spec/README.md
CHANGED
|
@@ -56,7 +56,10 @@ spec/
|
|
|
56
56
|
│ └── atr-to-nist-csf-2.0.md ← NIST CSF 2.0 (NIST IR 8596 Informative Reference draft)
|
|
57
57
|
│
|
|
58
58
|
├── category-registry/
|
|
59
|
-
│
|
|
59
|
+
│ ├── v1.0.yaml ← (new) versioned top-level category list
|
|
60
|
+
│ └── gemara/ ← (new) the registry as Gemara catalogs, generated
|
|
61
|
+
│ from v1.0.yaml; published as an OCI bundle
|
|
62
|
+
│ by publish-gemara.yml
|
|
60
63
|
│
|
|
61
64
|
├── schema/ ← (new) JSON Schemas
|
|
62
65
|
│ ├── rule.schema.json ← rule format JSON Schema
|
|
@@ -266,8 +269,8 @@ The spec is being prepared for:
|
|
|
266
269
|
|
|
267
270
|
1. **OASIS Open Project (primary)** as adjacent to CoSAI.
|
|
268
271
|
Target: Q3 2026 acceptance, Q1 2027 first Committee Specification.
|
|
269
|
-
2. **NIST CAISI
|
|
270
|
-
|
|
272
|
+
2. **NIST CAISI**, as a possible future citation venue only.
|
|
273
|
+
ATR has no engagement with CAISI.
|
|
271
274
|
3. **IETF (informational draft, transport / OTEL emission only)**
|
|
272
275
|
when reference implementations are stable.
|
|
273
276
|
|
package/spec/atr-event-v1.0.md
CHANGED
|
@@ -58,7 +58,7 @@ All conformant engines MUST emit these fields on every event.
|
|
|
58
58
|
| `@timestamp` | RFC 3339 UTC string | When the rule fired. |
|
|
59
59
|
| `atr.event_id` | UUID v7 (time-ordered) | Globally unique event identifier. |
|
|
60
60
|
| `atr.spec_version` | string | ATR spec version this event conforms to. v1.0 = `"1.0"`. |
|
|
61
|
-
| `atr.engine_id` | string | Identifier of the engine that produced the event. Format: `<vendor>/<product>/<version>`. Example: `atr/typescript-reference/3.1.0`, `
|
|
61
|
+
| `atr.engine_id` | string | Identifier of the engine that produced the event. Format: `<vendor>/<product>/<version>`. Example: `atr/typescript-reference/3.1.0`, `example-vendor/example-scanner/2.4.1`, `example-org/agent-gateway/2026.05`. |
|
|
62
62
|
|
|
63
63
|
### Rule attribution
|
|
64
64
|
|
|
@@ -105,7 +105,7 @@ All conformant engines MUST emit these fields on every event.
|
|
|
105
105
|
| Field | Type | Description |
|
|
106
106
|
|---|---|---|
|
|
107
107
|
| `evidence.observation_id` | UUID | Identifier for cross-reference into OSCAL `observation` records. Same as `atr.event_id` recommended unless an existing system has its own. |
|
|
108
|
-
| `evidence.signature` | base64 ed25519 | Signature over the canonical JSON encoding of this event. Signed by the engine's deployment-time key.
|
|
108
|
+
| `evidence.signature` | base64 ed25519 | Signature over the canonical JSON encoding of this event. Signed by the engine's deployment-time key. Intended for evidence chains such as EU AI Act record-keeping and OSCAL-based audit pipelines. May be omitted in `dev_mode` deployments. |
|
|
109
109
|
| `evidence.signature_key_id` | string | Identifier of the signing key. SHOULD reference a key registered with the deployer's CA. |
|
|
110
110
|
| `evidence.upstream_chain` | array \| null | When this event is part of a multi-agent chain (A2A), the upstream event IDs that led to this detection. Enables forensic chain reconstruction. |
|
|
111
111
|
|
|
@@ -132,12 +132,17 @@ is known).
|
|
|
132
132
|
| `agent.from_id` | string |
|
|
133
133
|
| `agent.to_id` | string |
|
|
134
134
|
| `agent.delegation_chain` | array of {agent_id, capability_grant, granted_by} |
|
|
135
|
-
| `agent.identity_assertion` |
|
|
136
|
-
|
|
137
|
-
The `agent.identity_assertion` field
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
the
|
|
135
|
+
| `agent.identity_assertion` | string \| null (a reference, never the assertion) |
|
|
136
|
+
|
|
137
|
+
The `agent.identity_assertion` field holds a reference to the identity
|
|
138
|
+
assertion that authorised the agent, written `<issuer>#<assertion id>`
|
|
139
|
+
(for a JWT, its `iss` and `jti`, or a SHA-256 digest of the token when it
|
|
140
|
+
has no `jti`). It never holds the assertion itself:
|
|
141
|
+
an assertion is a bearer credential, and the Forbidden fields section
|
|
142
|
+
below rules out raw tokens. The IETF AI agent auth drafts
|
|
143
|
+
(`draft-klrc-aiagent-auth-00`, `draft-ni-a2a-ai-agent-security-requirements-01`)
|
|
144
|
+
may settle a canonical identifier format; until then the reference form
|
|
145
|
+
above applies.
|
|
141
146
|
|
|
142
147
|
### Memory write detail (when `atr.matched_field == "memory_write"`)
|
|
143
148
|
|
package/spec/atr-method-v1.1.md
CHANGED
|
@@ -457,7 +457,7 @@ If `detection.condition` is absent, the default is `any of *` — Rule matches i
|
|
|
457
457
|
|
|
458
458
|
### 8.5 Evaluation Determinism
|
|
459
459
|
|
|
460
|
-
Trace evaluation MUST be deterministic for a fixed Trace input. Engines MUST NOT introduce randomization, time-of-day branching, or sampling into Trace evaluation. This is identical to SPEC §6.
|
|
460
|
+
Trace evaluation MUST be deterministic for a fixed Trace input. Engines MUST NOT introduce randomization, time-of-day branching, or sampling into Trace evaluation. This is identical to SPEC §6.5 and applies normatively.
|
|
461
461
|
|
|
462
462
|
### 8.6 Performance Considerations
|
|
463
463
|
|
|
@@ -508,7 +508,7 @@ This is the inverse direction of §6.5's calibration workflow: §6.5 ensures the
|
|
|
508
508
|
|
|
509
509
|
Rules MAY declare `references.nist_csf` and `references.etsi_ts_104223` to align with the two major sovereign cybersecurity frameworks for AI agents:
|
|
510
510
|
|
|
511
|
-
- `nist_csf`: NIST CSF 2.0 subcategory IDs (e.g., `DE.CM-09`, `PR.IR-01`).
|
|
511
|
+
- `nist_csf`: NIST CSF 2.0 subcategory IDs (e.g., `DE.CM-09`, `PR.IR-01`). Needed if a mapping is ever offered as a NIST IR 8596 Cyber AI Profile Informative Reference.
|
|
512
512
|
- `etsi_ts_104223`: ETSI TS 104 223 principle/sub-principle IDs (e.g., `P4.3`). ETSI TS 104 223 upstreamed UK NCSC's AI Cyber Code of Practice; this binding lets ATR Rules be cited in NCSC Implementation Guides.
|
|
513
513
|
|
|
514
514
|
Both fields are arrays of strings to permit multi-framework alignment per Rule.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Gemara catalogs for the ATR category registry
|
|
2
|
+
|
|
3
|
+
This directory holds the ATR category registry
|
|
4
|
+
([`../v1.0.yaml`](../v1.0.yaml)) expressed as [Gemara](https://github.com/gemaraproj/gemara)
|
|
5
|
+
catalogs, validated against the Gemara CUE schemas and published by
|
|
6
|
+
`.github/workflows/publish-gemara.yml` as a signed OCI bundle.
|
|
7
|
+
|
|
8
|
+
| File | Gemara type | What it holds |
|
|
9
|
+
|---|---|---|
|
|
10
|
+
| `atr-attack-vector-catalog.yaml` | `#VectorCatalog` | One vector per registry category. The vector id is the registry category id (which is also the `rules/` directory name) and the description is the registry's description. |
|
|
11
|
+
| `ai-agent-capability-catalog.yaml` | `#CapabilityCatalog` | Capabilities of AI agent systems that create the opportunity for the threats. |
|
|
12
|
+
| `atr-threat-catalog.yaml` | `#ThreatCatalog` | Threats at impact level, each mapped to the capabilities that enable it and the vectors that realize it. Every vector is reached by at least one threat. |
|
|
13
|
+
| `atr-detection-control-catalog.yaml` | `#ControlCatalog` | Detection controls an operator can apply with ATR, cut by when inspection happens. **Root artifact of the bundle.** |
|
|
14
|
+
|
|
15
|
+
## Why they live here
|
|
16
|
+
|
|
17
|
+
The vector catalog is a projection of the registry. When the projection and
|
|
18
|
+
the registry lived in different repositories, nothing stopped them drifting
|
|
19
|
+
apart. Here they sit side by side, and the catalogs are generated, not
|
|
20
|
+
hand-edited.
|
|
21
|
+
|
|
22
|
+
## Editing
|
|
23
|
+
|
|
24
|
+
The four catalogs are **generated**; do not edit them by hand. Edit one of the
|
|
25
|
+
two inputs and regenerate:
|
|
26
|
+
|
|
27
|
+
- [`../v1.0.yaml`](../v1.0.yaml), the registry: which categories exist, their
|
|
28
|
+
ids and their descriptions.
|
|
29
|
+
- [`src/editorial.yaml`](src/editorial.yaml): everything the registry does not
|
|
30
|
+
say (vector titles and grouping, and the capability, threat and control
|
|
31
|
+
catalogs).
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
npx tsx scripts/generate-gemara-catalogs.ts # regenerate
|
|
35
|
+
npx tsx scripts/generate-gemara-catalogs.ts --check # what CI checks
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The generator stops when the inputs disagree: a registry category with no
|
|
39
|
+
editorial entry, an editorial entry for a category the registry does not have,
|
|
40
|
+
a vector no threat reaches, or a reference to an id that does not exist.
|
|
41
|
+
`tests/gemara-catalogs.test.ts` fails when the committed catalogs differ from
|
|
42
|
+
the generator output.
|
|
43
|
+
|
|
44
|
+
## Validating
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
git clone --depth 1 --branch v1.6.0 https://github.com/gemaraproj/gemara.git /tmp/gemara
|
|
48
|
+
scripts/validate-gemara-catalogs.sh /tmp/gemara
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Each catalog is checked with `cue vet -c -d '#<metadata.type>'` against the
|
|
52
|
+
Gemara release named by `bundle.gemara-version` in `src/editorial.yaml`.
|
|
53
|
+
|
|
54
|
+
## Versioning
|
|
55
|
+
|
|
56
|
+
`bundle.version` in `src/editorial.yaml` is stamped as `metadata.version` on
|
|
57
|
+
all four catalogs and is the OCI tag. Its MAJOR.MINOR is the registry version
|
|
58
|
+
(the generator enforces this); PATCH moves with any change that alters a
|
|
59
|
+
generated catalog. A published version is never overwritten. While the
|
|
60
|
+
registry's `status` is `draft`, every catalog carries `draft: true`.
|
|
61
|
+
|
|
62
|
+
## Publication
|
|
63
|
+
|
|
64
|
+
`.github/workflows/publish-gemara.yml` publishes the bundle to
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
ghcr.io/agent-threat-rule/agent-threat-rules/gemara:<bundle version>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
using [`gemara-publish-action`](https://github.com/gemaraproj/gemara-publish-action).
|
|
71
|
+
Assembly starts at the control catalog and follows each catalog's `imports`
|
|
72
|
+
to the threat, capability and vector catalogs, so one bundle carries all four.
|
|
73
|
+
|
|
74
|
+
It runs by manual dispatch from a release tag. ATR release tags also start
|
|
75
|
+
it, but those runs are skipped until the repository variable
|
|
76
|
+
`GEMARA_PUBLISH_ON_TAGS` is `true`, so the first publish is always a manual
|
|
77
|
+
run. It never runs on a push to `main`. Rule changes move release tags, not the bundle: a release
|
|
78
|
+
whose catalogs are already published under their version pushes nothing, and
|
|
79
|
+
a release whose catalogs changed without a version bump fails before pushing.
|
|
80
|
+
|
|
81
|
+
The bundle is signed keyless with the workflow's GitHub OIDC identity; no key
|
|
82
|
+
is stored. To verify:
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
cosign verify ghcr.io/agent-threat-rule/agent-threat-rules/gemara:<version> \
|
|
86
|
+
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
|
|
87
|
+
--certificate-identity-regexp \
|
|
88
|
+
'^https://github\.com/Agent-Threat-Rule/agent-threat-rules/\.github/workflows/publish-gemara\.yml@refs/tags/v'
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Once a version is published, to reference a catalog from another Gemara artifact, use the bundle as the
|
|
92
|
+
mapping-reference `url` and the catalog's `metadata.id` as the reference `id`:
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
mapping-references:
|
|
96
|
+
- id: ATR-AI-AGENT-VECTOR-CATALOG
|
|
97
|
+
title: AI Agent Attack Vector Catalog (ATR)
|
|
98
|
+
version: "1.0.0"
|
|
99
|
+
url: oci://ghcr.io/agent-threat-rule/agent-threat-rules/gemara:1.0.0
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
License: MIT.
|