agent-threat-rules 2.2.1 → 3.0.5
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/README.md +365 -327
- package/dist/engine.d.ts +46 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +242 -1
- package/dist/engine.js.map +1 -1
- package/dist/eval/eval-harness.d.ts.map +1 -1
- package/dist/eval/eval-harness.js +9 -0
- package/dist/eval/eval-harness.js.map +1 -1
- package/dist/eval/run-hackaprompt-benchmark.js +9 -0
- package/dist/eval/run-hackaprompt-benchmark.js.map +1 -1
- package/dist/eval/run-pint-benchmark.js +9 -0
- package/dist/eval/run-pint-benchmark.js.map +1 -1
- package/dist/eval/skill-benchmark.d.ts +11 -0
- package/dist/eval/skill-benchmark.d.ts.map +1 -1
- package/dist/eval/skill-benchmark.js +57 -0
- package/dist/eval/skill-benchmark.js.map +1 -1
- package/dist/measurement/from-eval-harness.d.ts +70 -0
- package/dist/measurement/from-eval-harness.d.ts.map +1 -0
- package/dist/measurement/from-eval-harness.js +49 -0
- package/dist/measurement/from-eval-harness.js.map +1 -0
- package/dist/measurement/schema.d.ts +152 -0
- package/dist/measurement/schema.d.ts.map +1 -0
- package/dist/measurement/schema.js +178 -0
- package/dist/measurement/schema.js.map +1 -0
- package/dist/measurement/write.d.ts +64 -0
- package/dist/measurement/write.d.ts.map +1 -0
- package/dist/measurement/write.js +163 -0
- package/dist/measurement/write.js.map +1 -0
- package/dist/semantic-evaluator.d.ts +48 -0
- package/dist/semantic-evaluator.d.ts.map +1 -0
- package/dist/semantic-evaluator.js +107 -0
- package/dist/semantic-evaluator.js.map +1 -0
- package/dist/trace-evaluator.d.ts +22 -0
- package/dist/trace-evaluator.d.ts.map +1 -0
- package/dist/trace-evaluator.js +249 -0
- package/dist/trace-evaluator.js.map +1 -0
- package/dist/types.d.ts +143 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -3
- package/rules/agent-manipulation/ATR-2026-00552-goal-drift-after-pressure-injection.yaml +216 -0
- package/rules/context-exfiltration/ATR-2026-00524-claude-code-anthropic-base-url-credential-exfil.yaml +257 -0
- package/rules/context-exfiltration/ATR-2026-00548-cross-agent-session-context-leak.yaml +177 -0
- package/rules/excessive-autonomy/ATR-2026-00553-runaway-tool-loop-behavioral.yaml +174 -0
- package/rules/privilege-escalation/ATR-2026-00528-praisonai-auth-disabled-default.yaml +192 -0
- package/rules/privilege-escalation/ATR-2026-00539-crewai-codeinterpreter-sandbox-escape-rce.yaml +292 -0
- package/rules/privilege-escalation/ATR-2026-00546-crewai-json-loader-local-file-read.yaml +162 -0
- package/rules/privilege-escalation/ATR-2026-00547-crewai-rag-url-ssrf-bypass.yaml +167 -0
- package/rules/privilege-escalation/ATR-2026-00549-destructive-tool-without-human-approval.yaml +193 -0
- package/rules/privilege-escalation/ATR-2026-00551-cross-conversation-memory-write.yaml +198 -0
- package/rules/prompt-injection/ATR-2026-00535-windsurf-ide-zero-click-prompt-injection.yaml +199 -0
- package/rules/prompt-injection/ATR-2026-00550-untrusted-retrieval-to-privileged-tool.yaml +199 -0
- package/rules/skill-compromise/ATR-2026-00123-skill-overreach-permissions.yaml +5 -2
- package/rules/skill-compromise/ATR-2026-00523-claude-code-hooks-session-start-pre-trust-rce.yaml +221 -0
- package/rules/skill-compromise/ATR-2026-00525-mini-shai-hulud-gh-token-monitor-persistence.yaml +220 -0
- package/rules/skill-compromise/ATR-2026-00527-skill-silent-git-remote-mirror-exfiltration.yaml +201 -0
- package/rules/tool-poisoning/ATR-2026-00526-claude-code-shell-metachar-in-double-quoted-path.yaml +167 -0
- package/rules/tool-poisoning/ATR-2026-00529-litellm-proxy-sqli-cisa-kev.yaml +158 -0
- package/rules/tool-poisoning/ATR-2026-00530-ms-agent-shell-tool-unsanitized-argv-rce.yaml +184 -0
- package/rules/tool-poisoning/ATR-2026-00531-praisonai-unauthenticated-agent-api.yaml +174 -0
- package/rules/tool-poisoning/ATR-2026-00532-apache-doris-mcp-sql-injection.yaml +155 -0
- package/rules/tool-poisoning/ATR-2026-00533-apache-pinot-mcp-unauthenticated-takeover.yaml +151 -0
- package/rules/tool-poisoning/ATR-2026-00534-alibaba-rds-mcp-unauthenticated-metadata-exfil.yaml +155 -0
- package/rules/tool-poisoning/ATR-2026-00536-nginx-ui-mcp-unauthenticated-command-execution.yaml +199 -0
- package/rules/tool-poisoning/ATR-2026-00537-fastmcp-server-name-cmd-injection-windows.yaml +226 -0
- package/rules/tool-poisoning/ATR-2026-00538-langchain-chatchat-mcp-stdio-unauthenticated-rce.yaml +244 -0
- package/rules/tool-poisoning/ATR-2026-00540-praisonai-parse-mcp-command-cli-injection.yaml +186 -0
- package/rules/tool-poisoning/ATR-2026-00541-agent-zero-mcp-config-command-injection.yaml +183 -0
- package/rules/tool-poisoning/ATR-2026-00542-upsonic-mcp-command-allowlist-bypass.yaml +166 -0
- package/rules/tool-poisoning/ATR-2026-00543-litellm-mcp-server-argv-injection.yaml +168 -0
- package/rules/tool-poisoning/ATR-2026-00544-praisonai-pth-file-path-traversal-rce.yaml +172 -0
- package/rules/tool-poisoning/ATR-2026-00545-praisonai-tool-override-unauth-rce.yaml +170 -0
- package/spec/README.md +279 -0
- package/spec/atr-correlation-v1.0.md +281 -0
- package/spec/atr-event-v1.0.md +294 -0
- package/spec/atr-language-detection-v1.0.md +218 -0
- package/spec/atr-method-v1.1.md +557 -0
- package/spec/atr-profile-v1.0.md +307 -0
- package/spec/atr-schema.yaml +279 -8
- package/spec/category-registry/v1.0.yaml +200 -0
- package/spec/conformance/README.md +244 -0
- package/spec/conformance/SIGNING.md +191 -0
- package/spec/conformance/baseline/fixtures/ATR-2026-00001-tp-001/expected.json +36 -0
- package/spec/conformance/baseline/fixtures/ATR-2026-00001-tp-001/input.json +16 -0
- package/spec/conformance/baseline/fixtures/README.md +120 -0
- package/spec/conformance/baseline/manifest.json +56 -0
- package/spec/conformance/expected-results.schema.json +121 -0
- package/spec/external-registries/cccs-yara.md +142 -0
- package/spec/internet-drafts/draft-lin-atr-core-00.html +1925 -0
- package/spec/internet-drafts/draft-lin-atr-core-00.md +288 -0
- package/spec/internet-drafts/draft-lin-atr-core-00.txt +560 -0
- package/spec/internet-drafts/draft-lin-atr-core-00.xml +424 -0
- package/spec/mappings/README.md +43 -0
- package/spec/mappings/atr-to-nist-csf-2.0.md +234 -0
- package/spec/schema/correlation.schema.json +144 -0
- package/spec/schema/event.schema.json +233 -0
- package/spec/schema/profile.schema.json +196 -0
- package/spec/schema/rule.schema.json +224 -0
- package/spec/stix-extension/README.md +76 -13
- package/spec/stix-extension/examples/atr-rule-trace-method-example.json +85 -0
- package/spec/stix-extension/extension-definition.json +23 -3
- package/spec/stix-extension/x-atr-rule-schema.json +107 -11
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# ATR Event Format v1.0 — OpenTelemetry-aligned
|
|
2
|
+
|
|
3
|
+
> **STATUS: PROPOSED v1.0 — NOT YET RATIFIED.** This specification describes
|
|
4
|
+
> a target event format for community comment. The current TypeScript production
|
|
5
|
+
> engine continues to emit its existing event shape. Adopters should NOT
|
|
6
|
+
> migrate to this format until ratification. See `STANDARDIZATION-STATUS.md`
|
|
7
|
+
> for full status.
|
|
8
|
+
|
|
9
|
+
**Status:** Draft for AEP-002 ratification — NOT RATIFIED
|
|
10
|
+
**Date:** 2026-05-25
|
|
11
|
+
**License:** CC BY 4.0
|
|
12
|
+
**Required by (on ratification):** Conformant engine output, downstream SIEM/SOAR ingestion, EU AI Act Article 50 evidence chains
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Purpose
|
|
17
|
+
|
|
18
|
+
When a conformant ATR engine fires a rule, it emits an **event**.
|
|
19
|
+
This document specifies the event format.
|
|
20
|
+
|
|
21
|
+
Three requirements forced the design:
|
|
22
|
+
|
|
23
|
+
1. **OpenTelemetry alignment.** Existing agent-observability stacks
|
|
24
|
+
(LangSmith, Logfire, Datadog APM, Honeycomb) already ingest OTEL
|
|
25
|
+
spans. An ATR event that maps cleanly into an OTEL span attribute
|
|
26
|
+
set is consumable by these stacks zero-modification.
|
|
27
|
+
|
|
28
|
+
2. **EU AI Act Article 50 evidence.** Article 50 obligations (apply
|
|
29
|
+
2 August 2026) require deployer-side evidence of AI interaction.
|
|
30
|
+
ATR events must carry sufficient identity + provenance + signature
|
|
31
|
+
data to land in an audit binder without supplementary munging.
|
|
32
|
+
|
|
33
|
+
3. **NIST AI RMF MEASURE function.** OSCAL assessment-result format
|
|
34
|
+
requires structured observation records. ATR events must be
|
|
35
|
+
one-to-one mappable to OSCAL `observation` entries so audit
|
|
36
|
+
pipelines (AWS Config, RegScale, Centraleyes) can ingest natively.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Event JSON Schema reference
|
|
41
|
+
|
|
42
|
+
Machine-readable schema: `spec/schema/event.schema.json`.
|
|
43
|
+
|
|
44
|
+
This document is the normative prose specification. In case of
|
|
45
|
+
discrepancy between the two, **the prose spec governs**; the JSON
|
|
46
|
+
Schema must be corrected to match (via AEP).
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Required fields
|
|
51
|
+
|
|
52
|
+
All conformant engines MUST emit these fields on every event.
|
|
53
|
+
|
|
54
|
+
### Identification
|
|
55
|
+
|
|
56
|
+
| Field | Type | Description |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| `@timestamp` | RFC 3339 UTC string | When the rule fired. |
|
|
59
|
+
| `atr.event_id` | UUID v7 (time-ordered) | Globally unique event identifier. |
|
|
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`, `cisco/ai-defense/2.4.1`, `microsoft/agent-governance-toolkit/2026.05`. |
|
|
62
|
+
|
|
63
|
+
### Rule attribution
|
|
64
|
+
|
|
65
|
+
| Field | Type | Description |
|
|
66
|
+
|---|---|---|
|
|
67
|
+
| `atr.rule_id` | string | The matched rule ID. Format per ATR Rule Format Spec § 2: `ATR-YYYY-NNNNN` for canonical rules, `ATR-XX-YYYY-NNNNN` for sovereign-prefixed rules. |
|
|
68
|
+
| `atr.rule_version` | integer | The `rule_version` field from the matched rule's YAML. |
|
|
69
|
+
| `atr.rule_status` | enum | `draft` / `experimental` / `stable` / `deprecated` per rule's `status` field. |
|
|
70
|
+
| `atr.rule_maturity` | enum | `draft` / `test` / `stable` per rule's `maturity` field. |
|
|
71
|
+
| `atr.rule_review_status` | enum | `unreviewed` / `community_reviewed` / `tsc_approved` per governance/CHARTER.md § 5. |
|
|
72
|
+
|
|
73
|
+
### Detection result
|
|
74
|
+
|
|
75
|
+
| Field | Type | Description |
|
|
76
|
+
|---|---|---|
|
|
77
|
+
| `atr.severity` | enum | `critical` / `high` / `medium` / `low` / `informational` from matched rule. |
|
|
78
|
+
| `atr.category` | string | Rule's top-level category from `spec/category-registry/v1.0.yaml`, OR `unknown` if engine encountered unregistered category (per forward-compatibility rule). |
|
|
79
|
+
| `atr.subcategory` | string \| null | Optional finer classification from rule's `tags.subcategory`. |
|
|
80
|
+
| `atr.confidence` | number 0.0-1.0 | Engine's confidence in the match. For deterministic regex matches: `1.0`. For probabilistic / ML-judge matches (future): per the rule's declared semantics. |
|
|
81
|
+
| `atr.matched_field` | enum | Which field triggered the match. One of: `user_input`, `agent_output`, `tool_call`, `tool_response`, `skill_content`, `mcp_exchange`, `memory_write`, `multi_agent_message`. |
|
|
82
|
+
| `atr.matched_value_redacted` | string | The matched portion of the input. **MUST be redacted by default** — sensitive content (api keys, PII) replaced with `[REDACTED:type:length]`. Engines MAY disable redaction in `forensic_mode`, which MUST be explicitly enabled per deployment. |
|
|
83
|
+
|
|
84
|
+
### Agent + session context
|
|
85
|
+
|
|
86
|
+
| Field | Type | Description |
|
|
87
|
+
|---|---|---|
|
|
88
|
+
| `agent.id` | string | Stable identifier of the agent instance. |
|
|
89
|
+
| `agent.platform` | string | Agent platform name. Common values: `claude_code`, `cursor`, `openclaw`, `codex_cli`, `windsurf`, `gemini_cli`, `cline`, `continue`, `langchain`, `autogen`, `crewai`. Engines SHOULD use this canonical set; unknown values are accepted. |
|
|
90
|
+
| `agent.platform_version` | string \| null | Version of the agent platform. |
|
|
91
|
+
| `session.id` | string | Stable identifier of the agent session. |
|
|
92
|
+
| `service.name` | string | OTEL semantic convention. The service that hosts the agent. |
|
|
93
|
+
| `service.version` | string | OTEL semantic convention. |
|
|
94
|
+
|
|
95
|
+
### Response
|
|
96
|
+
|
|
97
|
+
| Field | Type | Description |
|
|
98
|
+
|---|---|---|
|
|
99
|
+
| `atr.response_action` | array of enum | Recommended response actions from rule's `response.actions`. Subset of: `block_input`, `block_output`, `redact`, `alert`, `snapshot`, `quarantine`, `terminate_session`. |
|
|
100
|
+
| `atr.response_taken` | array of enum | What the engine / agent platform actually did. May differ from recommended if local policy overrides. |
|
|
101
|
+
| `atr.response_threshold_met` | boolean | Whether the rule's `auto_response_threshold` was met. |
|
|
102
|
+
|
|
103
|
+
### Evidence + provenance
|
|
104
|
+
|
|
105
|
+
| Field | Type | Description |
|
|
106
|
+
|---|---|---|
|
|
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. Required for EU AI Act Article 50 evidence chains and NIST AI RMF audit pipelines. May be omitted in `dev_mode` deployments. |
|
|
109
|
+
| `evidence.signature_key_id` | string | Identifier of the signing key. SHOULD reference a key registered with the deployer's CA. |
|
|
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
|
+
|
|
112
|
+
## Optional fields
|
|
113
|
+
|
|
114
|
+
### Tool call detail (when `atr.matched_field == "tool_call"` or `"tool_response"`)
|
|
115
|
+
|
|
116
|
+
| Field | Type |
|
|
117
|
+
|---|---|
|
|
118
|
+
| `tool.name` | string |
|
|
119
|
+
| `tool.args` | object (redacted) |
|
|
120
|
+
| `tool.privilege_class` | string |
|
|
121
|
+
| `tool.target_jurisdiction` | ISO 3166-1 alpha-2 \| `und` |
|
|
122
|
+
|
|
123
|
+
The `tool.target_jurisdiction` field is for EU AI Act + GDPR cross-
|
|
124
|
+
border data-flow audit. Required when the engine knows where the
|
|
125
|
+
tool's effect lands (e.g., an `s3.put` tool call where bucket region
|
|
126
|
+
is known).
|
|
127
|
+
|
|
128
|
+
### Multi-agent chain detail (when `atr.matched_field == "multi_agent_message"`)
|
|
129
|
+
|
|
130
|
+
| Field | Type |
|
|
131
|
+
|---|---|
|
|
132
|
+
| `agent.from_id` | string |
|
|
133
|
+
| `agent.to_id` | string |
|
|
134
|
+
| `agent.delegation_chain` | array of {agent_id, capability_grant, granted_by} |
|
|
135
|
+
| `agent.identity_assertion` | JWT \| null |
|
|
136
|
+
|
|
137
|
+
The `agent.identity_assertion` field anticipates the IETF AI agent
|
|
138
|
+
auth drafts (`draft-klrc-aiagent-auth-00`, `draft-ni-a2a-ai-agent-
|
|
139
|
+
security-requirements-01`) — once those reach RFC, the field carries
|
|
140
|
+
the canonical assertion format.
|
|
141
|
+
|
|
142
|
+
### Memory write detail (when `atr.matched_field == "memory_write"`)
|
|
143
|
+
|
|
144
|
+
| Field | Type |
|
|
145
|
+
|---|---|
|
|
146
|
+
| `memory.store_id` | string |
|
|
147
|
+
| `memory.write_key` | string |
|
|
148
|
+
| `memory.persistence_scope` | enum | `session` \| `user` \| `agent_global` |
|
|
149
|
+
|
|
150
|
+
This captures the SpAIware (Rehberger 2026) attack class — memory-
|
|
151
|
+
poisoning persistence across sessions.
|
|
152
|
+
|
|
153
|
+
### Sovereign attestation (when rule ID is sovereign-prefixed)
|
|
154
|
+
|
|
155
|
+
| Field | Type |
|
|
156
|
+
|---|---|
|
|
157
|
+
| `atr.sovereign_attestation` | object {signer, signature, ca_chain} |
|
|
158
|
+
|
|
159
|
+
Required when the matched rule carries a sovereign prefix
|
|
160
|
+
(`ATR-DE-`, `ATR-SG-`, `ATR-TW-`, etc.) per governance/CHARTER.md § 8.2.
|
|
161
|
+
Engines MUST validate the attestation against the TSC-maintained
|
|
162
|
+
sovereign key registry before honoring the event's elevated trust.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Forbidden fields
|
|
167
|
+
|
|
168
|
+
The following MUST NOT appear in an ATR event under any circumstance:
|
|
169
|
+
|
|
170
|
+
- Raw user PII (names, addresses, phone numbers). PII detected by the
|
|
171
|
+
rule is referenced via `atr.matched_value_redacted` with type and
|
|
172
|
+
length only.
|
|
173
|
+
- Raw API keys / credentials / tokens. Always redacted.
|
|
174
|
+
- Full prompt / response text in `matched_value_redacted`. Only the
|
|
175
|
+
matched fragment, redacted.
|
|
176
|
+
|
|
177
|
+
Engines that operate in `forensic_mode` MAY emit additional fields
|
|
178
|
+
for in-flight audit, but these MUST be explicitly enabled per
|
|
179
|
+
deployment AND clearly distinguished in event metadata.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## OpenTelemetry mapping (informative)
|
|
184
|
+
|
|
185
|
+
For OTEL ingestion, ATR events map to spans:
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
OpenTelemetry Span ATR Event Field
|
|
189
|
+
───────────────────── ──────────────────────────
|
|
190
|
+
span.name → "atr.detection." + atr.category
|
|
191
|
+
span.kind → "INTERNAL"
|
|
192
|
+
span.start_time → @timestamp
|
|
193
|
+
span.duration → engine's evaluation time
|
|
194
|
+
span.status.code → "ERROR" if atr.severity in [critical, high]
|
|
195
|
+
"OK" otherwise
|
|
196
|
+
span.attributes.atr.* → all atr.* fields
|
|
197
|
+
span.attributes.agent.* → all agent.* fields
|
|
198
|
+
span.attributes.session.id → session.id
|
|
199
|
+
span.attributes.service.name → service.name
|
|
200
|
+
span.events → [{name: "atr.rule_matched",
|
|
201
|
+
attributes: {rule_id, matched_field}}]
|
|
202
|
+
span.resource.attributes → service.name, service.version
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
This mapping is informative; downstream tools may consume the raw
|
|
206
|
+
ATR event JSON without OTEL conversion.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## OSCAL assessment-result mapping (informative)
|
|
211
|
+
|
|
212
|
+
For NIST AI RMF + OSCAL pipelines, each ATR event maps to one OSCAL
|
|
213
|
+
`observation`:
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
OSCAL observation ATR Event Field
|
|
217
|
+
────────────────── ────────────────────
|
|
218
|
+
uuid → evidence.observation_id (UUID v7)
|
|
219
|
+
collected → @timestamp
|
|
220
|
+
title → "ATR rule " + atr.rule_id + " matched"
|
|
221
|
+
description → human-readable from rule's `description` field
|
|
222
|
+
methods → ["AUTOMATED"]
|
|
223
|
+
types → ["finding"]
|
|
224
|
+
subjects → [{type: "component",
|
|
225
|
+
subject-uuid: agent.id}]
|
|
226
|
+
relevant-evidence → [{href: link to atr.event_id,
|
|
227
|
+
description: "ATR detection event"}]
|
|
228
|
+
remarks → free-form, may include atr.response_taken
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
This mapping enables zero-write integration with OSCAL profile-based
|
|
232
|
+
audit. ATR events stream into OSCAL assessment-result format
|
|
233
|
+
without manual munging.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Example event
|
|
238
|
+
|
|
239
|
+
```json
|
|
240
|
+
{
|
|
241
|
+
"@timestamp": "2026-05-25T08:14:32.182Z",
|
|
242
|
+
"atr.event_id": "01927e2d-7b32-7c41-9e84-3b8f2a1e9c54",
|
|
243
|
+
"atr.spec_version": "1.0",
|
|
244
|
+
"atr.engine_id": "atr/typescript-reference/3.1.0",
|
|
245
|
+
"atr.rule_id": "ATR-2026-00525",
|
|
246
|
+
"atr.rule_version": 1,
|
|
247
|
+
"atr.rule_status": "stable",
|
|
248
|
+
"atr.rule_maturity": "test",
|
|
249
|
+
"atr.rule_review_status": "community_reviewed",
|
|
250
|
+
"atr.severity": "critical",
|
|
251
|
+
"atr.category": "skill-compromise",
|
|
252
|
+
"atr.subcategory": "supply-chain-worm",
|
|
253
|
+
"atr.confidence": 1.0,
|
|
254
|
+
"atr.matched_field": "skill_content",
|
|
255
|
+
"atr.matched_value_redacted": "[REDACTED:identifier:18] persistence daemon installed",
|
|
256
|
+
"atr.response_action": ["block_input", "alert", "snapshot"],
|
|
257
|
+
"atr.response_taken": ["block_input", "alert"],
|
|
258
|
+
"atr.response_threshold_met": true,
|
|
259
|
+
"agent.id": "agt-customer-12345-claude-prod-01",
|
|
260
|
+
"agent.platform": "claude_code",
|
|
261
|
+
"agent.platform_version": "1.8.4",
|
|
262
|
+
"session.id": "sess-2026-05-25-bk9a8x",
|
|
263
|
+
"service.name": "panguard-scan",
|
|
264
|
+
"service.version": "1.4.13",
|
|
265
|
+
"evidence.observation_id": "01927e2d-7b32-7c41-9e84-3b8f2a1e9c54",
|
|
266
|
+
"evidence.signature": "MEQCIBdJpL3zEoXxKj9F/qqM8DxFJp7Q...",
|
|
267
|
+
"evidence.signature_key_id": "kid:panguard-scan-prod-2026-05",
|
|
268
|
+
"evidence.upstream_chain": null
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Versioning
|
|
275
|
+
|
|
276
|
+
This spec is at v1.0. Field additions are minor-version-compatible
|
|
277
|
+
(v1.x) and do not break conformant consumers. Field removals or
|
|
278
|
+
semantic changes are major-version (v2.0) and require an AEP.
|
|
279
|
+
|
|
280
|
+
Conformant engines MUST emit `atr.spec_version` so consumers can
|
|
281
|
+
adapt to future versions.
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## References
|
|
286
|
+
|
|
287
|
+
- OpenTelemetry semantic conventions: https://opentelemetry.io/docs/specs/semconv/
|
|
288
|
+
- OSCAL Assessment Results: https://pages.nist.gov/OSCAL/concepts/layer/assessment/assessment-results/
|
|
289
|
+
- EU AI Act Article 50: https://artificialintelligenceact.eu/article/50/
|
|
290
|
+
- UUID v7 (time-ordered): https://datatracker.ietf.org/doc/rfc9562/
|
|
291
|
+
- Ed25519 signing: https://datatracker.ietf.org/doc/rfc8032/
|
|
292
|
+
- IETF AI agent auth draft: https://datatracker.ietf.org/doc/html/draft-klrc-aiagent-auth-00
|
|
293
|
+
- ATR Rule Format Spec v1.0: ATR-SPEC-v1.md
|
|
294
|
+
- ATR Category Registry v1.0: spec/category-registry/v1.0.yaml
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# ATR Language Detection Algorithm v1.0
|
|
2
|
+
|
|
3
|
+
> **STATUS: PROPOSED v1.0 — NOT YET RATIFIED.** This specification describes
|
|
4
|
+
> a target algorithm for community comment. The current TypeScript production
|
|
5
|
+
> engine continues to use its existing per-rule language detection. See
|
|
6
|
+
> `STANDARDIZATION-STATUS.md` for full status.
|
|
7
|
+
|
|
8
|
+
**Status:** Draft for AEP-001 ratification — NOT RATIFIED
|
|
9
|
+
**Date:** 2026-05-25
|
|
10
|
+
**License:** CC BY 4.0
|
|
11
|
+
**Required by (on ratification):** Any rule that declares `condition.language` (i.e., a per-language regex condition)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Why this spec exists
|
|
16
|
+
|
|
17
|
+
ATR rules support per-language conditions:
|
|
18
|
+
|
|
19
|
+
```yaml
|
|
20
|
+
detection:
|
|
21
|
+
conditions:
|
|
22
|
+
- field: user_input
|
|
23
|
+
operator: regex
|
|
24
|
+
language: en
|
|
25
|
+
value: "ignore (?:all )?previous instructions"
|
|
26
|
+
- field: user_input
|
|
27
|
+
operator: regex
|
|
28
|
+
language: zh-Hant
|
|
29
|
+
value: "(?:忽略|無視)(?:前面所有|之前所有|所有先前)的?指示"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
If different conformant engines disagree on which language a given
|
|
33
|
+
input belongs to, the **same input fires different rules in different
|
|
34
|
+
engines**. The rule corpus becomes non-portable. This is the
|
|
35
|
+
detection-standard equivalent of a heisenbug.
|
|
36
|
+
|
|
37
|
+
This document specifies a **deterministic algorithm** that all
|
|
38
|
+
conformant engines MUST implement. Any conformant ATR engine running
|
|
39
|
+
this algorithm on the same input must return the same language code.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Algorithm specification
|
|
44
|
+
|
|
45
|
+
### Input
|
|
46
|
+
|
|
47
|
+
- `text`: a Unicode string of arbitrary length, encoded UTF-8.
|
|
48
|
+
|
|
49
|
+
### Output
|
|
50
|
+
|
|
51
|
+
- A two-letter ISO 639-1 language code from the supported set, OR the
|
|
52
|
+
three-letter ISO 639-3 code `und` (undetermined).
|
|
53
|
+
|
|
54
|
+
### Supported languages (v1.0)
|
|
55
|
+
|
|
56
|
+
| Code | Language | Unicode blocks (primary) |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| `en` | English | Basic Latin |
|
|
59
|
+
| `zh-Hant` | Traditional Chinese | CJK Unified Ideographs (script-tagged Traditional via Unihan kZVariant inversion when available; defaults to Traditional for Taiwan / Hong Kong corpora) |
|
|
60
|
+
| `zh-Hans` | Simplified Chinese | CJK Unified Ideographs (script-tagged Simplified) |
|
|
61
|
+
| `ja` | Japanese | Hiragana + Katakana + CJK Unified Ideographs |
|
|
62
|
+
| `es` | Spanish | Latin Extended-A + Latin-1 Supplement subset |
|
|
63
|
+
| `ar` | Arabic | Arabic + Arabic Supplement |
|
|
64
|
+
|
|
65
|
+
Additional languages may be added via AEP. Engines that do not
|
|
66
|
+
implement a language MUST report `und` for inputs in that language,
|
|
67
|
+
NOT fall back to a default.
|
|
68
|
+
|
|
69
|
+
### Algorithm (deterministic, single-pass)
|
|
70
|
+
|
|
71
|
+
```text
|
|
72
|
+
function detectLanguage(text: string) -> string {
|
|
73
|
+
if length(text) == 0:
|
|
74
|
+
return "und"
|
|
75
|
+
|
|
76
|
+
// Phase 1: Unicode block frequency
|
|
77
|
+
blockCounts = empty histogram
|
|
78
|
+
totalCodepoints = 0
|
|
79
|
+
for codepoint in iterateUnicodeCodepoints(text):
|
|
80
|
+
if isWhitespace(codepoint) or isPunctuation(codepoint):
|
|
81
|
+
continue
|
|
82
|
+
blockCounts[unicodeBlockOf(codepoint)] += 1
|
|
83
|
+
totalCodepoints += 1
|
|
84
|
+
|
|
85
|
+
if totalCodepoints == 0:
|
|
86
|
+
return "und"
|
|
87
|
+
|
|
88
|
+
// Phase 2: dominant-block heuristic
|
|
89
|
+
THRESHOLD_DOMINANT = 0.60
|
|
90
|
+
dominantBlock, dominantCount = argmax(blockCounts)
|
|
91
|
+
if dominantCount / totalCodepoints < THRESHOLD_DOMINANT:
|
|
92
|
+
return classifyMixedScript(blockCounts, totalCodepoints)
|
|
93
|
+
|
|
94
|
+
// Phase 3: block-to-language mapping
|
|
95
|
+
switch dominantBlock:
|
|
96
|
+
case BASIC_LATIN:
|
|
97
|
+
// English is the default Latin script. Spanish detected only
|
|
98
|
+
// if Latin-1 Supplement subset (¿ ¡ ñ á é í ó ú) makes up
|
|
99
|
+
// ≥1.5% of codepoints.
|
|
100
|
+
if (count(BASIC_LATIN) + count(LATIN_1_SUPPLEMENT)) / totalCodepoints >= 0.85:
|
|
101
|
+
if hasSpanishMarkers(text) >= 0.015 * totalCodepoints:
|
|
102
|
+
return "es"
|
|
103
|
+
return "en"
|
|
104
|
+
return classifyMixedScript(blockCounts, totalCodepoints)
|
|
105
|
+
|
|
106
|
+
case CJK_UNIFIED_IDEOGRAPHS, CJK_UNIFIED_IDEOGRAPHS_EXT_A, ...:
|
|
107
|
+
// Disambiguate Chinese variants and Japanese
|
|
108
|
+
kanaCount = count(HIRAGANA) + count(KATAKANA)
|
|
109
|
+
if kanaCount >= 0.10 * totalCodepoints:
|
|
110
|
+
return "ja"
|
|
111
|
+
// Distinguish Hans vs Hant via Unihan kSimplifiedVariant /
|
|
112
|
+
// kTraditionalVariant lookups on sampled CJK codepoints.
|
|
113
|
+
// Tie-breaker: default to zh-Hant.
|
|
114
|
+
return distinguishHansHant(text)
|
|
115
|
+
|
|
116
|
+
case HIRAGANA, KATAKANA:
|
|
117
|
+
return "ja"
|
|
118
|
+
|
|
119
|
+
case ARABIC, ARABIC_SUPPLEMENT:
|
|
120
|
+
return "ar"
|
|
121
|
+
|
|
122
|
+
default:
|
|
123
|
+
return "und"
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function classifyMixedScript(blockCounts, totalCodepoints) -> string {
|
|
127
|
+
// Mixed-script inputs (common when English technical terms are
|
|
128
|
+
// embedded in CJK or Arabic text):
|
|
129
|
+
// 1. If any single non-Latin script block ≥ 40% → return that script's language
|
|
130
|
+
// 2. Else → return the language whose block has highest count,
|
|
131
|
+
// breaking ties by ISO 639-1 alphabetical order (ar, en, es, ja, zh-Hans, zh-Hant)
|
|
132
|
+
// The alphabetical tie-break is the deterministic fallback.
|
|
133
|
+
...
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Specific normative requirements for conformant implementations
|
|
138
|
+
|
|
139
|
+
1. **Whitespace and punctuation are excluded from the frequency count.** Only "content codepoints" enter the histogram.
|
|
140
|
+
2. **The 0.60 dominance threshold is normative.** Engines MUST NOT alter it without an AEP-level change.
|
|
141
|
+
3. **Hans/Hant distinction is based on Unihan property data**, not on heuristic character set membership. Engines MUST use the Unicode Consortium's Unihan database for kSimplifiedVariant / kTraditionalVariant lookups.
|
|
142
|
+
4. **Japanese detection is anchored on kana presence ≥ 10%**, not just on CJK ideograph presence. This prevents mis-classifying Chinese-only text as Japanese.
|
|
143
|
+
5. **Spanish vs English is anchored on Spanish-specific markers** (`¿`, `¡`, `ñ`, accented vowels). Engines MUST require ≥ 1.5% of codepoints to be Spanish markers before classifying as `es`.
|
|
144
|
+
6. **Tie-breaking is deterministic** via alphabetical ISO 639-1 ordering. No randomness, no implementation-defined behavior.
|
|
145
|
+
7. **Unknown blocks default to `und`.** No fuzzy fallback. Rules tagged for unsupported languages do not fire on inputs the engine cannot classify.
|
|
146
|
+
|
|
147
|
+
### Edge cases (normative)
|
|
148
|
+
|
|
149
|
+
| Input | Required output |
|
|
150
|
+
|---|---|
|
|
151
|
+
| Empty string | `und` |
|
|
152
|
+
| All whitespace | `und` |
|
|
153
|
+
| Single English word | `en` |
|
|
154
|
+
| Single Spanish word with ñ | `es` |
|
|
155
|
+
| Single Japanese kana character | `ja` |
|
|
156
|
+
| Single CJK ideograph (no kana, no Unihan disambiguation possible) | `zh-Hant` (tie-break default) |
|
|
157
|
+
| Mixed 60% English + 40% Chinese | `en` (60% dominance reached) |
|
|
158
|
+
| Mixed 50% English + 50% Chinese | `en` (alphabetical tie-break: `en` < `zh-Hans`) |
|
|
159
|
+
| Pure punctuation | `und` |
|
|
160
|
+
| Emoji-only | `und` (emoji are not content codepoints for language classification) |
|
|
161
|
+
|
|
162
|
+
### Verification
|
|
163
|
+
|
|
164
|
+
A conformant engine MUST pass the language-detection test corpus at
|
|
165
|
+
`spec/conformance/language-detection/`. The corpus contains
|
|
166
|
+
≥ 200 fixture inputs with expected outputs. Disagreement on any fixture
|
|
167
|
+
is a spec violation.
|
|
168
|
+
|
|
169
|
+
### Reasoning (non-normative)
|
|
170
|
+
|
|
171
|
+
This algorithm is designed for **detection-rule dispatch**, not
|
|
172
|
+
high-accuracy NLP. Two design choices follow:
|
|
173
|
+
|
|
174
|
+
1. **Speed over recall**: ATR engines must classify in < 1 ms p99
|
|
175
|
+
for typical inputs to meet the < 100 ms total runtime budget per
|
|
176
|
+
rule. Block-frequency analysis is O(n) over codepoints and meets
|
|
177
|
+
this bound easily. NLP-grade detectors (FastText, langdetect)
|
|
178
|
+
require model loading and stochastic inference; both violate the
|
|
179
|
+
determinism requirement.
|
|
180
|
+
|
|
181
|
+
2. **Determinism over accuracy on edge cases**: Two engines must
|
|
182
|
+
agree, even if both are slightly wrong on edge cases. A 90% accurate
|
|
183
|
+
deterministic algorithm is more useful than a 95% accurate
|
|
184
|
+
probabilistic one because the spec's portability promise depends on
|
|
185
|
+
bit-for-bit agreement.
|
|
186
|
+
|
|
187
|
+
The algorithm is intentionally narrow: 6 languages, single-pass,
|
|
188
|
+
explicit thresholds. AEPs may add languages or refine thresholds, but
|
|
189
|
+
the v1.0 algorithm above is the conformance baseline.
|
|
190
|
+
|
|
191
|
+
### Test vectors
|
|
192
|
+
|
|
193
|
+
Engines testing for conformance must reproduce these outputs exactly.
|
|
194
|
+
Full fixture set in `spec/conformance/language-detection/v1.0.json`.
|
|
195
|
+
|
|
196
|
+
| # | Input (UTF-8) | Expected output |
|
|
197
|
+
|---|---|---|
|
|
198
|
+
| 1 | `""` | `und` |
|
|
199
|
+
| 2 | `" "` | `und` |
|
|
200
|
+
| 3 | `"hello world"` | `en` |
|
|
201
|
+
| 4 | `"Por favor, ¿podría ayudarme?"` | `es` |
|
|
202
|
+
| 5 | `"こんにちは、世界"` | `ja` |
|
|
203
|
+
| 6 | `"忽略所有先前指示"` | `zh-Hant` (tie-break) |
|
|
204
|
+
| 7 | `"忽略所有先前的指示"` | `zh-Hant` (tie-break; "的" is shared simplified/traditional) |
|
|
205
|
+
| 8 | `"忽略所有以前指示"` | `zh-Hans` (Unihan kSimplifiedVariant evidence) |
|
|
206
|
+
| 9 | `"تجاهل جميع التعليمات السابقة"` | `ar` |
|
|
207
|
+
| 10 | `"@mistralai/mistralai 中的 prompt injection"` | `zh-Hant` (Chinese > 40% non-Latin) |
|
|
208
|
+
| 11 | `"call ATR-2026-00525"` | `en` |
|
|
209
|
+
| 12 | `" "` + `` (ZWS) | `und` |
|
|
210
|
+
| 13 | `"😀😎🚀"` (emoji only) | `und` |
|
|
211
|
+
|
|
212
|
+
### References
|
|
213
|
+
|
|
214
|
+
- ISO 639-1 / ISO 639-3 language code registry: https://iso639-3.sil.org/
|
|
215
|
+
- Unicode Block names: https://www.unicode.org/Public/UCD/latest/ucd/Blocks.txt
|
|
216
|
+
- Unihan Database: https://www.unicode.org/charts/unihan.html
|
|
217
|
+
- Spanish markers: derived from the Real Academia Española orthography guide
|
|
218
|
+
- Why deterministic over probabilistic for spec dispatch: discussed in `STANDARD-THREAT-MODEL.md` Attacker class 1 (rule poisoner) which exploits any non-determinism in engine behaviour
|