@sponsio/sdk 0.1.0-alpha.3
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/contracts/capability/credentials.yaml +188 -0
- package/contracts/capability/database.yaml +141 -0
- package/contracts/capability/filesystem-strict.yaml +101 -0
- package/contracts/capability/filesystem.yaml +239 -0
- package/contracts/capability/self-modify.yaml +152 -0
- package/contracts/capability/shell.yaml +246 -0
- package/contracts/capability/subagent.yaml +184 -0
- package/contracts/core/llm_safety.yaml +130 -0
- package/contracts/core/runaway.yaml +43 -0
- package/contracts/core/universal.yaml +34 -0
- package/contracts/incident/claude-code-secret-bypass.yaml +197 -0
- package/contracts/incident/cursor-railway-wipe.yaml +164 -0
- package/contracts/incident/mcp-composition.yaml +418 -0
- package/contracts/incident/openclaw.yaml +500 -0
- package/contracts/incident/subagent-escape.yaml +177 -0
- package/contracts/premium/.gitkeep +7 -0
- package/dist/contract.d.ts +59 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +97 -0
- package/dist/contract.js.map +1 -0
- package/dist/core/config-loader.d.ts +90 -0
- package/dist/core/config-loader.d.ts.map +1 -0
- package/dist/core/config-loader.js +530 -0
- package/dist/core/config-loader.js.map +1 -0
- package/dist/core/evaluator.d.ts +15 -0
- package/dist/core/evaluator.d.ts.map +1 -0
- package/dist/core/evaluator.js +87 -0
- package/dist/core/evaluator.js.map +1 -0
- package/dist/core/formula.d.ts +103 -0
- package/dist/core/formula.d.ts.map +1 -0
- package/dist/core/formula.js +167 -0
- package/dist/core/formula.js.map +1 -0
- package/dist/core/grounding.d.ts +43 -0
- package/dist/core/grounding.d.ts.map +1 -0
- package/dist/core/grounding.js +269 -0
- package/dist/core/grounding.js.map +1 -0
- package/dist/core/nl-parser.d.ts +12 -0
- package/dist/core/nl-parser.d.ts.map +1 -0
- package/dist/core/nl-parser.js +152 -0
- package/dist/core/nl-parser.js.map +1 -0
- package/dist/core/pack-loader.d.ts +15 -0
- package/dist/core/pack-loader.d.ts.map +1 -0
- package/dist/core/pack-loader.js +220 -0
- package/dist/core/pack-loader.js.map +1 -0
- package/dist/core/parser.d.ts +19 -0
- package/dist/core/parser.d.ts.map +1 -0
- package/dist/core/parser.js +235 -0
- package/dist/core/parser.js.map +1 -0
- package/dist/core/pattern-factory.d.ts +38 -0
- package/dist/core/pattern-factory.d.ts.map +1 -0
- package/dist/core/pattern-factory.js +222 -0
- package/dist/core/pattern-factory.js.map +1 -0
- package/dist/core/patterns.d.ts +67 -0
- package/dist/core/patterns.d.ts.map +1 -0
- package/dist/core/patterns.js +585 -0
- package/dist/core/patterns.js.map +1 -0
- package/dist/core/session-log.d.ts +91 -0
- package/dist/core/session-log.d.ts.map +1 -0
- package/dist/core/session-log.js +196 -0
- package/dist/core/session-log.js.map +1 -0
- package/dist/core/spans.d.ts +92 -0
- package/dist/core/spans.d.ts.map +1 -0
- package/dist/core/spans.js +148 -0
- package/dist/core/spans.js.map +1 -0
- package/dist/core/sto.d.ts +120 -0
- package/dist/core/sto.d.ts.map +1 -0
- package/dist/core/sto.js +147 -0
- package/dist/core/sto.js.map +1 -0
- package/dist/index.d.ts +267 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +540 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/claude-agent.d.ts +29 -0
- package/dist/integrations/claude-agent.d.ts.map +1 -0
- package/dist/integrations/claude-agent.js +40 -0
- package/dist/integrations/claude-agent.js.map +1 -0
- package/dist/integrations/google-adk.d.ts +27 -0
- package/dist/integrations/google-adk.d.ts.map +1 -0
- package/dist/integrations/google-adk.js +81 -0
- package/dist/integrations/google-adk.js.map +1 -0
- package/dist/integrations/langchain.d.ts +26 -0
- package/dist/integrations/langchain.d.ts.map +1 -0
- package/dist/integrations/langchain.js +74 -0
- package/dist/integrations/langchain.js.map +1 -0
- package/dist/integrations/openai-agents.d.ts +45 -0
- package/dist/integrations/openai-agents.d.ts.map +1 -0
- package/dist/integrations/openai-agents.js +81 -0
- package/dist/integrations/openai-agents.js.map +1 -0
- package/dist/integrations/openai.d.ts +24 -0
- package/dist/integrations/openai.d.ts.map +1 -0
- package/dist/integrations/openai.js +77 -0
- package/dist/integrations/openai.js.map +1 -0
- package/dist/integrations/vercel-ai.d.ts +38 -0
- package/dist/integrations/vercel-ai.d.ts.map +1 -0
- package/dist/integrations/vercel-ai.js +107 -0
- package/dist/integrations/vercel-ai.js.map +1 -0
- package/dist/render/components.d.ts +23 -0
- package/dist/render/components.d.ts.map +1 -0
- package/dist/render/components.js +133 -0
- package/dist/render/components.js.map +1 -0
- package/dist/render/derive.d.ts +23 -0
- package/dist/render/derive.d.ts.map +1 -0
- package/dist/render/derive.js +92 -0
- package/dist/render/derive.js.map +1 -0
- package/dist/render/session-view.d.ts +19 -0
- package/dist/render/session-view.d.ts.map +1 -0
- package/dist/render/session-view.js +236 -0
- package/dist/render/session-view.js.map +1 -0
- package/dist/render/tokens.d.ts +44 -0
- package/dist/render/tokens.d.ts.map +1 -0
- package/dist/render/tokens.js +72 -0
- package/dist/render/tokens.js.map +1 -0
- package/package.json +83 -0
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# Sponsio contract pack for OpenClaw (née Clawdbot / Moltbot)
|
|
3
|
+
# Target: OpenClaw local gateway, v2026.2.12 or newer
|
|
4
|
+
#
|
|
5
|
+
# Schema conventions used in this file:
|
|
6
|
+
# - `desc:` — human-readable explanation (rendering only)
|
|
7
|
+
# - `E:` with `pattern:` — structured, uses sponsio.patterns.library
|
|
8
|
+
# - `E:` with `ltl:` — naked LTL over grounding atoms (needs ltl parser)
|
|
9
|
+
# - `A:` — used when the contract is conditional
|
|
10
|
+
#
|
|
11
|
+
# Every constraint carries a `source:` tag:
|
|
12
|
+
# library:tier0.* portable across agents (candidate for universal.yaml)
|
|
13
|
+
# library:tier1.<cap> capability-indexed (shell / browser / messaging / ...)
|
|
14
|
+
# incident:CVE-... derived from a specific 2026 OpenClaw incident
|
|
15
|
+
# incident:<name> derived from a named campaign (ClawHavoc, etc.)
|
|
16
|
+
#
|
|
17
|
+
# Incident key:
|
|
18
|
+
# CVE-2026-25253 1-click WebSocket RCE via gateway token exfil
|
|
19
|
+
# CVE-2026-22708 indirect prompt injection via browsed content
|
|
20
|
+
# CVE-2026-29607 "allow-always" approval wrapper bypass
|
|
21
|
+
# CVE-2026-28460 approval allowlist bypass via shell line-continuation
|
|
22
|
+
# CVE-2026-32048 sandbox inheritance bypass
|
|
23
|
+
# issue-12515 coding-agent --yolo flag (no sandbox, no audit)
|
|
24
|
+
# ClawHavoc 1,184 malicious ClawHub skills (AMOS stealer)
|
|
25
|
+
# weather-skill skill that exfiltrated ~/.clawdbot/.env
|
|
26
|
+
# wired-guilttrip emotional manipulation → agent self-sabotage
|
|
27
|
+
# =============================================================================
|
|
28
|
+
|
|
29
|
+
version: "1"
|
|
30
|
+
|
|
31
|
+
judge:
|
|
32
|
+
fallback_mode: allow
|
|
33
|
+
circuit_breaker: true
|
|
34
|
+
|
|
35
|
+
# -----------------------------------------------------------------------------
|
|
36
|
+
# Tool inventory. `confirm_reconfirmed` and `audit_log` are marker tools —
|
|
37
|
+
# they don't execute anything; integrations emit them when a human confirmation
|
|
38
|
+
# or audit-log write occurs, so contracts can reference them.
|
|
39
|
+
# -----------------------------------------------------------------------------
|
|
40
|
+
tools:
|
|
41
|
+
- name: exec
|
|
42
|
+
description: "Shell command execution (built-in, sandbox-gated)"
|
|
43
|
+
params: "command: str"
|
|
44
|
+
- name: read
|
|
45
|
+
description: "Read file contents"
|
|
46
|
+
params: "path: str"
|
|
47
|
+
- name: write
|
|
48
|
+
description: "Create or overwrite a file"
|
|
49
|
+
params: "path: str, content: str"
|
|
50
|
+
- name: edit
|
|
51
|
+
description: "In-place edit of an existing file"
|
|
52
|
+
params: "path: str, patch: str"
|
|
53
|
+
- name: apply_patch
|
|
54
|
+
description: "Apply a unified diff (gated by tools.exec.applyPatch)"
|
|
55
|
+
params: "path: str, diff: str"
|
|
56
|
+
- name: web_fetch
|
|
57
|
+
description: "Browser skill — fetch URL, return rendered content to context"
|
|
58
|
+
params: "url: str"
|
|
59
|
+
- name: send_message
|
|
60
|
+
description: "Send a message on a channel"
|
|
61
|
+
params: "channel: str, channel_type: str, body: str"
|
|
62
|
+
- name: install_skill
|
|
63
|
+
description: "Install a ClawHub skill into the workspace"
|
|
64
|
+
params: "name: str, version: str"
|
|
65
|
+
- name: run_skill
|
|
66
|
+
description: "Invoke an installed skill"
|
|
67
|
+
params: "name: str, args: str"
|
|
68
|
+
- name: cron_create
|
|
69
|
+
description: "Schedule a recurring autonomous job"
|
|
70
|
+
params: "cron_id: str, schedule: str, task: str"
|
|
71
|
+
- name: cron_complete
|
|
72
|
+
description: "Marker: a cron job finished"
|
|
73
|
+
- name: gateway_config_set
|
|
74
|
+
description: "Admin API: mutate gateway config (sandbox, approvals, host)"
|
|
75
|
+
params: "key: str, value: str"
|
|
76
|
+
- name: hook_register
|
|
77
|
+
description: "Register an event hook or webhook"
|
|
78
|
+
params: "event: str, target: str"
|
|
79
|
+
- name: confirm_reconfirmed
|
|
80
|
+
description: "Marker: human operator confirmed the next action"
|
|
81
|
+
- name: audit_log
|
|
82
|
+
description: "Marker: an audit-log entry was written"
|
|
83
|
+
|
|
84
|
+
# -----------------------------------------------------------------------------
|
|
85
|
+
agents:
|
|
86
|
+
# Template agent id — replaced at include time by the including config.
|
|
87
|
+
# Matches the convention used by the other packs (core/*, capability/*).
|
|
88
|
+
"*":
|
|
89
|
+
contracts:
|
|
90
|
+
|
|
91
|
+
# =====================================================================
|
|
92
|
+
# § 0 — Tier 0 universal (portable; candidates for a future universal.yaml)
|
|
93
|
+
# =====================================================================
|
|
94
|
+
|
|
95
|
+
- desc: "No prompt injection in any LLM response"
|
|
96
|
+
E:
|
|
97
|
+
pattern: injection_free
|
|
98
|
+
context_scope: full_trace
|
|
99
|
+
source: "library:tier0.adversarial"
|
|
100
|
+
beta: 0.95
|
|
101
|
+
|
|
102
|
+
- desc: "No jailbreak compliance in any LLM response"
|
|
103
|
+
E:
|
|
104
|
+
pattern: jailbreak_free
|
|
105
|
+
source: "library:tier0.adversarial"
|
|
106
|
+
beta: 0.9
|
|
107
|
+
|
|
108
|
+
- desc: "No harmful advice or facilitation of real-world harm"
|
|
109
|
+
E:
|
|
110
|
+
pattern: harmful
|
|
111
|
+
source: "library:tier0.harm"
|
|
112
|
+
beta: 0.95
|
|
113
|
+
|
|
114
|
+
- desc: "Response free of contextual PII"
|
|
115
|
+
E:
|
|
116
|
+
pattern: semantic_pii_free
|
|
117
|
+
source: "library:tier0.privacy"
|
|
118
|
+
beta: 0.95
|
|
119
|
+
|
|
120
|
+
- desc: "Response free of toxic or harassing language"
|
|
121
|
+
E:
|
|
122
|
+
pattern: toxic_free
|
|
123
|
+
source: "library:tier0.harm"
|
|
124
|
+
beta: 0.9
|
|
125
|
+
|
|
126
|
+
- desc: "Total token usage bounded per session"
|
|
127
|
+
E:
|
|
128
|
+
pattern: token_budget
|
|
129
|
+
args: [200000, "total"]
|
|
130
|
+
source: "library:tier0.budget"
|
|
131
|
+
|
|
132
|
+
- desc: "No consecutive repetition of the same tool beyond a sane limit"
|
|
133
|
+
E:
|
|
134
|
+
pattern: loop_detection
|
|
135
|
+
args: [exec, 5]
|
|
136
|
+
source: "library:tier0.runaway"
|
|
137
|
+
|
|
138
|
+
# =====================================================================
|
|
139
|
+
# § 1 — Shell blast radius
|
|
140
|
+
# Targets: CVE-2026-28460, issue-12515, wired-guilttrip
|
|
141
|
+
# =====================================================================
|
|
142
|
+
|
|
143
|
+
- desc: "Ban classic destructive shell patterns"
|
|
144
|
+
E:
|
|
145
|
+
pattern: arg_blacklist
|
|
146
|
+
args:
|
|
147
|
+
- exec
|
|
148
|
+
- command
|
|
149
|
+
- ["rm\\s+-rf\\s+/", "curl[^|]+\\|\\s*(bash|sh)", "wget[^|]+\\|\\s*(bash|sh)", ":\\(\\)\\s*\\{\\s*:\\s*\\|\\s*:\\s*&\\s*\\}\\s*;\\s*:"]
|
|
150
|
+
source: "library:tier1.shell"
|
|
151
|
+
|
|
152
|
+
# Narrow form — see capability/shell.yaml §1 for the rationale.
|
|
153
|
+
# We flag backslash-newline only when chained into a privileged
|
|
154
|
+
# command token; unconstrained `\<newline>` appears legitimately
|
|
155
|
+
# in Makefiles / Dockerfiles / shell scripts.
|
|
156
|
+
- desc: "Block shell line-continuation chained into a privileged command (CVE-2026-28460)"
|
|
157
|
+
E:
|
|
158
|
+
pattern: arg_blacklist
|
|
159
|
+
args:
|
|
160
|
+
- exec
|
|
161
|
+
- command
|
|
162
|
+
- - "\\\\\\n\\s*(sudo|su\\s|rm\\s+-[rRf]|dd\\s+if=|chmod\\s|chown\\s|mount\\s|curl\\s|wget\\s)"
|
|
163
|
+
- "\\\\\\r?\\n\\s*(sudo|su\\s|rm\\s+-[rRf]|dd\\s+if=|chmod\\s|chown\\s|mount\\s|curl\\s|wget\\s)"
|
|
164
|
+
source: "incident:CVE-2026-28460"
|
|
165
|
+
|
|
166
|
+
- desc: "Block --yolo / --no-confirm flags on skill invocations"
|
|
167
|
+
E:
|
|
168
|
+
pattern: arg_blacklist
|
|
169
|
+
args: [run_skill, args, ["--yolo\\b", "--no-confirm\\b"]]
|
|
170
|
+
source: "incident:issue-12515"
|
|
171
|
+
|
|
172
|
+
- desc: "Reject exec commands with suspiciously long inline payloads"
|
|
173
|
+
E:
|
|
174
|
+
pattern: arg_length_limit
|
|
175
|
+
args: [exec, command, 2000]
|
|
176
|
+
source: "library:tier1.shell"
|
|
177
|
+
|
|
178
|
+
- desc: "Blast-radius cap: at most 10 exec calls per session"
|
|
179
|
+
E:
|
|
180
|
+
pattern: rate_limit
|
|
181
|
+
args: [exec, 10]
|
|
182
|
+
source: "library:tier1.shell"
|
|
183
|
+
|
|
184
|
+
# Conditional: sudo / dd / chmod 777 require an immediately preceding
|
|
185
|
+
# confirm_reconfirmed. The existing must_confirm pattern is unconditional
|
|
186
|
+
# on tool name; we want it conditional on the argument content. LTL form.
|
|
187
|
+
- desc: "Privileged exec patterns require a preceding human confirmation"
|
|
188
|
+
E:
|
|
189
|
+
ltl: 'G(called_with(exec, "(sudo|dd\\s+if=|chmod\\s+777)") -> count(confirm_reconfirmed) >= count_with(exec, "(sudo|dd\\s+if=|chmod\\s+777)"))'
|
|
190
|
+
source: "library:tier1.shell"
|
|
191
|
+
|
|
192
|
+
# =====================================================================
|
|
193
|
+
# § 2 — Gateway config tampering (CVE-2026-25253 kill-chain terminus)
|
|
194
|
+
# The attack chain ends with flipping `exec.approvals.set=off` and
|
|
195
|
+
# `tools.exec.host=gateway` via the admin API. Tripwire every write.
|
|
196
|
+
# =====================================================================
|
|
197
|
+
|
|
198
|
+
- desc: "Ban gateway config keys that disable sandbox or approvals"
|
|
199
|
+
E:
|
|
200
|
+
pattern: arg_blacklist
|
|
201
|
+
args:
|
|
202
|
+
- gateway_config_set
|
|
203
|
+
- key
|
|
204
|
+
- ["exec\\.approvals", "tools\\.exec\\.host", "sandbox", "agent\\.skipBootstrap"]
|
|
205
|
+
source: "incident:CVE-2026-25253"
|
|
206
|
+
|
|
207
|
+
- desc: "Admin config changes extremely rare"
|
|
208
|
+
E:
|
|
209
|
+
pattern: rate_limit
|
|
210
|
+
args: [gateway_config_set, 1]
|
|
211
|
+
source: "incident:CVE-2026-25253"
|
|
212
|
+
|
|
213
|
+
- desc: "Every admin config change must be followed by an audit-log entry"
|
|
214
|
+
E:
|
|
215
|
+
pattern: always_followed_by
|
|
216
|
+
args: [gateway_config_set, audit_log]
|
|
217
|
+
source: "incident:CVE-2026-25253"
|
|
218
|
+
|
|
219
|
+
# =====================================================================
|
|
220
|
+
# § 3 — Filesystem scope (credentials + self-modifying memory)
|
|
221
|
+
# Targets: weather-skill, AMOS, agent editing its own bootstrap files
|
|
222
|
+
# =====================================================================
|
|
223
|
+
|
|
224
|
+
- desc: "Reads restricted to workspace and /tmp"
|
|
225
|
+
E:
|
|
226
|
+
pattern: scope_limit
|
|
227
|
+
args:
|
|
228
|
+
- read
|
|
229
|
+
- ["<workspace>/", "/tmp/"]
|
|
230
|
+
source: "library:tier1.filesystem"
|
|
231
|
+
|
|
232
|
+
- desc: "Hard-deny reads of sensitive paths (SSH, AWS, OpenClaw secrets)"
|
|
233
|
+
E:
|
|
234
|
+
pattern: arg_blacklist
|
|
235
|
+
args:
|
|
236
|
+
- read
|
|
237
|
+
- path
|
|
238
|
+
- ["\\.ssh/", "\\.aws/credentials", "\\.clawdbot/\\.env", "\\.openclaw/openclaw\\.json", "Keychain"]
|
|
239
|
+
source: "incident:weather-skill"
|
|
240
|
+
|
|
241
|
+
- desc: "Writes restricted to workspace"
|
|
242
|
+
E:
|
|
243
|
+
pattern: scope_limit
|
|
244
|
+
args:
|
|
245
|
+
- write
|
|
246
|
+
- ["<workspace>/"]
|
|
247
|
+
source: "library:tier1.filesystem"
|
|
248
|
+
|
|
249
|
+
# Bootstrap-file self-modification — conditional on the path.
|
|
250
|
+
- desc: "Self-modification of bootstrap files requires confirmation"
|
|
251
|
+
E:
|
|
252
|
+
ltl: 'G(called_with(edit, "(AGENTS|SOUL|IDENTITY|USER)\\.md") -> count(confirm_reconfirmed) >= count_with(edit, "(AGENTS|SOUL|IDENTITY|USER)\\.md"))'
|
|
253
|
+
source: "library:tier1.persistent_memory"
|
|
254
|
+
|
|
255
|
+
- desc: "apply_patch against SOUL.md always requires confirmation"
|
|
256
|
+
E:
|
|
257
|
+
ltl: 'G(called_with(apply_patch, "SOUL\\.md") -> count(confirm_reconfirmed) >= count_with(apply_patch, "SOUL\\.md"))'
|
|
258
|
+
source: "library:tier1.persistent_memory"
|
|
259
|
+
|
|
260
|
+
- desc: "No blind edits — must read a path before editing it"
|
|
261
|
+
E:
|
|
262
|
+
pattern: must_precede
|
|
263
|
+
args: [read, edit]
|
|
264
|
+
source: "library:tier1.filesystem"
|
|
265
|
+
|
|
266
|
+
- desc: "Bootstrap files preserve integrity"
|
|
267
|
+
E:
|
|
268
|
+
pattern: data_intact
|
|
269
|
+
args:
|
|
270
|
+
- "bootstrap_files"
|
|
271
|
+
- ["<workspace>/AGENTS.md", "<workspace>/SOUL.md", "<workspace>/IDENTITY.md", "<workspace>/USER.md"]
|
|
272
|
+
source: "library:tier1.persistent_memory"
|
|
273
|
+
|
|
274
|
+
# =====================================================================
|
|
275
|
+
# § 4 — Skills supply chain (ClawHavoc)
|
|
276
|
+
# Koi Security: 1,184 of ~3,000 ClawHub skills were malicious; ClickFix
|
|
277
|
+
# social-engineered users into running payload commands as prerequisites.
|
|
278
|
+
# =====================================================================
|
|
279
|
+
|
|
280
|
+
- desc: "Install and run are segregated"
|
|
281
|
+
E:
|
|
282
|
+
pattern: segregation_of_duty
|
|
283
|
+
args: [install_skill, run_skill]
|
|
284
|
+
source: "incident:ClawHavoc"
|
|
285
|
+
|
|
286
|
+
- desc: "Every skill install followed by an audit-log entry"
|
|
287
|
+
E:
|
|
288
|
+
pattern: always_followed_by
|
|
289
|
+
args: [install_skill, audit_log]
|
|
290
|
+
source: "incident:ClawHavoc"
|
|
291
|
+
|
|
292
|
+
- desc: "At most 3 skill installs per session"
|
|
293
|
+
E:
|
|
294
|
+
pattern: rate_limit
|
|
295
|
+
args: [install_skill, 3]
|
|
296
|
+
source: "incident:ClawHavoc"
|
|
297
|
+
|
|
298
|
+
- desc: "Cooldown of 10 steps between skill installs"
|
|
299
|
+
E:
|
|
300
|
+
pattern: cooldown
|
|
301
|
+
args: [install_skill, 10]
|
|
302
|
+
source: "incident:ClawHavoc"
|
|
303
|
+
|
|
304
|
+
- desc: "Typosquat defense — ban ClawHub official-CLI impersonators"
|
|
305
|
+
E:
|
|
306
|
+
pattern: arg_blacklist
|
|
307
|
+
args:
|
|
308
|
+
- install_skill
|
|
309
|
+
- name
|
|
310
|
+
- ["^openclaw-agent$", "^clawhub-cli$", "^openclaw-cli$"]
|
|
311
|
+
source: "incident:ClawHavoc"
|
|
312
|
+
|
|
313
|
+
# After a fresh install, no exec for 5 steps (VirusTotal scan window).
|
|
314
|
+
# No clean pattern fit; LTL expresses windowed inhibition.
|
|
315
|
+
- desc: "After install_skill, no exec for at least 5 steps"
|
|
316
|
+
E:
|
|
317
|
+
ltl: "G(called(install_skill) -> (!called(exec) & X(!called(exec) & X(!called(exec) & X(!called(exec) & X(!called(exec)))))))"
|
|
318
|
+
source: "incident:ClawHavoc"
|
|
319
|
+
|
|
320
|
+
# =====================================================================
|
|
321
|
+
# § 5 — Indirect prompt injection (CVE-2026-22708, lethal trifecta)
|
|
322
|
+
# Web / email / calendar content feeds the context window unsanitized.
|
|
323
|
+
# Core move: after any untrusted ingest, privileged sinks need re-confirm.
|
|
324
|
+
# =====================================================================
|
|
325
|
+
|
|
326
|
+
# The sink list covers high-privilege actions (code execution,
|
|
327
|
+
# skill install, patch apply). `send_message` is NOT in the
|
|
328
|
+
# default sink set because the standard RAG pattern is
|
|
329
|
+
# `web_fetch → reason → send_message`, and a re-confirm
|
|
330
|
+
# requirement on every retrieval-augmented reply would cripple
|
|
331
|
+
# chat/assistant workloads. Teams whose `send_message` routes
|
|
332
|
+
# to external (non-owner) channels should add it via
|
|
333
|
+
# customized: — see the commented example below.
|
|
334
|
+
- desc: "After untrusted content ingest, sensitive sinks require re-confirmation"
|
|
335
|
+
E:
|
|
336
|
+
pattern: untrusted_source_gate
|
|
337
|
+
args:
|
|
338
|
+
- [web_fetch]
|
|
339
|
+
- [exec, install_skill, apply_patch]
|
|
340
|
+
source: "incident:CVE-2026-22708"
|
|
341
|
+
|
|
342
|
+
# Uncomment to extend the gate to outbound messages when your
|
|
343
|
+
# agent's `send_message` reaches third parties:
|
|
344
|
+
#
|
|
345
|
+
# - desc: "After untrusted ingest, outbound messages also gated"
|
|
346
|
+
# E:
|
|
347
|
+
# pattern: untrusted_source_gate
|
|
348
|
+
# args:
|
|
349
|
+
# - [web_fetch]
|
|
350
|
+
# - [send_message]
|
|
351
|
+
# source: "incident:CVE-2026-22708"
|
|
352
|
+
|
|
353
|
+
- desc: "Web browsing loops bounded — prevents attacker-controlled chain-redirect ingestion"
|
|
354
|
+
E:
|
|
355
|
+
pattern: rate_limit
|
|
356
|
+
args: [web_fetch, 25]
|
|
357
|
+
source: "incident:CVE-2026-22708"
|
|
358
|
+
|
|
359
|
+
# Taint: content flagged as coming from web_fetch must not end up flowing
|
|
360
|
+
# to external recipients. Depends on integration emitting
|
|
361
|
+
# data_write(key=fetched_content, contains=[untrusted]) after web_fetch.
|
|
362
|
+
- desc: "Content tainted as untrusted must not flow to external recipients"
|
|
363
|
+
E:
|
|
364
|
+
ltl: "G(contains(untrusted) -> !flow(<agent>, external))"
|
|
365
|
+
source: "incident:CVE-2026-22708"
|
|
366
|
+
|
|
367
|
+
# =====================================================================
|
|
368
|
+
# § 6 — Data-leak prevention (weather-skill, AMOS)
|
|
369
|
+
# =====================================================================
|
|
370
|
+
|
|
371
|
+
- desc: "Data read from filesystem must not flow to send_message"
|
|
372
|
+
E:
|
|
373
|
+
pattern: no_data_leak
|
|
374
|
+
args: [read, send_message]
|
|
375
|
+
source: "incident:weather-skill"
|
|
376
|
+
|
|
377
|
+
- desc: "Data read from filesystem must not flow to install_skill"
|
|
378
|
+
E:
|
|
379
|
+
pattern: no_data_leak
|
|
380
|
+
args: [read, install_skill]
|
|
381
|
+
source: "incident:AMOS"
|
|
382
|
+
|
|
383
|
+
- desc: "Block outbound exfiltration primitives in shell"
|
|
384
|
+
E:
|
|
385
|
+
pattern: arg_blacklist
|
|
386
|
+
args:
|
|
387
|
+
- exec
|
|
388
|
+
- command
|
|
389
|
+
- ["nc\\s+-e", "bash\\s+-i\\s*>&\\s*/dev/tcp", "curl[^|]+\\s--upload-file", "python.*socket\\.socket"]
|
|
390
|
+
source: "incident:AMOS"
|
|
391
|
+
|
|
392
|
+
# =====================================================================
|
|
393
|
+
# § 7 — Multi-channel / identity isolation
|
|
394
|
+
# OpenClaw has no per-channel privilege model. Owner DM ≠ group ≠
|
|
395
|
+
# non-allowlisted channel, but the gateway treats them identically.
|
|
396
|
+
# =====================================================================
|
|
397
|
+
|
|
398
|
+
# Group-channel messages must not be followed by exec. LTL expresses the
|
|
399
|
+
# channel_type=group condition cleanly.
|
|
400
|
+
- desc: "Messages from group channels cannot trigger exec"
|
|
401
|
+
E:
|
|
402
|
+
ltl: 'G(called_with(send_message, "channel_type=group") -> !F(called(exec)))'
|
|
403
|
+
source: "library:tier1.messaging"
|
|
404
|
+
|
|
405
|
+
- desc: "Per-session cap on outbound messages"
|
|
406
|
+
E:
|
|
407
|
+
pattern: rate_limit
|
|
408
|
+
args: [send_message, 30]
|
|
409
|
+
source: "library:tier1.messaging"
|
|
410
|
+
|
|
411
|
+
- desc: "Cron creation is idempotent within a session"
|
|
412
|
+
E:
|
|
413
|
+
pattern: idempotent
|
|
414
|
+
args: [cron_create]
|
|
415
|
+
source: "library:tier1.scheduler"
|
|
416
|
+
|
|
417
|
+
- desc: "Every cron_create must eventually be matched by cron_complete"
|
|
418
|
+
E:
|
|
419
|
+
pattern: required_steps_completion
|
|
420
|
+
args: [cron_create, [cron_complete]]
|
|
421
|
+
source: "library:tier1.scheduler"
|
|
422
|
+
|
|
423
|
+
- desc: "Hook / webhook registration requires human confirmation"
|
|
424
|
+
E:
|
|
425
|
+
pattern: must_confirm
|
|
426
|
+
args: [hook_register]
|
|
427
|
+
source: "library:tier1.persistence"
|
|
428
|
+
|
|
429
|
+
# =====================================================================
|
|
430
|
+
# § 8 — Approval integrity (CVE-2026-29607 wrapper bypass, generalized)
|
|
431
|
+
# "allow-always" persists at wrapper level; swap inner payload → no
|
|
432
|
+
# re-prompt. Generalization: one confirmation cannot authorize two
|
|
433
|
+
# destructive actions. Count-dominance is a proxy for principal identity
|
|
434
|
+
# (we don't yet have a `principal()` atom).
|
|
435
|
+
#
|
|
436
|
+
# Both rules below are gated on the assumption that the agent actually
|
|
437
|
+
# emits `confirm_reconfirmed` events — without that wiring the guarantee
|
|
438
|
+
# is vacuous-false at every exec/apply_patch call, which would drown
|
|
439
|
+
# observe-mode reports. The CVE the rules defend against is a
|
|
440
|
+
# confirmation-REUSE attack, which only makes sense when confirmation
|
|
441
|
+
# is integrated in the first place.
|
|
442
|
+
# =====================================================================
|
|
443
|
+
|
|
444
|
+
- desc: "Confirmations cannot be reused — count(confirm) must dominate count(exec)"
|
|
445
|
+
A: "called `confirm_reconfirmed`"
|
|
446
|
+
E:
|
|
447
|
+
ltl: "G(called(exec) -> count(confirm_reconfirmed) >= count(exec))"
|
|
448
|
+
source: "incident:CVE-2026-29607"
|
|
449
|
+
|
|
450
|
+
- desc: "Same for apply_patch — each apply needs its own confirm"
|
|
451
|
+
A: "called `confirm_reconfirmed`"
|
|
452
|
+
E:
|
|
453
|
+
ltl: "G(called(apply_patch) -> count(confirm_reconfirmed) >= count(apply_patch))"
|
|
454
|
+
source: "incident:CVE-2026-29607"
|
|
455
|
+
|
|
456
|
+
# =====================================================================
|
|
457
|
+
# § 9 — Capability-specific stochastic atoms
|
|
458
|
+
# =====================================================================
|
|
459
|
+
|
|
460
|
+
- desc: "After web_fetch, responses stay grounded in the fetched content"
|
|
461
|
+
A: "called `web_fetch`"
|
|
462
|
+
E:
|
|
463
|
+
pattern: hallucination_free
|
|
464
|
+
args: ["{fetched_content}"]
|
|
465
|
+
source: "library:tier1.browser"
|
|
466
|
+
beta: 0.9
|
|
467
|
+
|
|
468
|
+
- desc: "Agent stays within its declared scope"
|
|
469
|
+
E:
|
|
470
|
+
pattern: scope_respect
|
|
471
|
+
args: ["local personal assistant — file, shell, messaging, browsing on behalf of owner"]
|
|
472
|
+
source: "library:tier0.scope"
|
|
473
|
+
beta: 0.85
|
|
474
|
+
|
|
475
|
+
- desc: "Agent does not agree to disable its own safety constraints (anti-guilttrip)"
|
|
476
|
+
E:
|
|
477
|
+
pattern: jailbreak_free
|
|
478
|
+
source: "incident:wired-guilttrip"
|
|
479
|
+
beta: 0.95
|
|
480
|
+
|
|
481
|
+
# -----------------------------------------------------------------------------
|
|
482
|
+
# Notes on what this file deliberately does NOT cover
|
|
483
|
+
# -----------------------------------------------------------------------------
|
|
484
|
+
#
|
|
485
|
+
# - `principal(X)` / `authorized_by(human)` atom-level distinctions:
|
|
486
|
+
# § 8 uses count-dominance as a proxy, which catches *reuse* of confirmations
|
|
487
|
+
# but not the identity of the confirmer. A proper fix needs a grounding-layer
|
|
488
|
+
# change (new event type or principal atom).
|
|
489
|
+
#
|
|
490
|
+
# - Per-channel trust levels beyond channel_type=group:
|
|
491
|
+
# "allowFrom" whitelist, owner DM vs. third-party Telegram, etc. require an
|
|
492
|
+
# integration that emits channel identity on every send_message event. The
|
|
493
|
+
# current `called_with(send_message, "channel_type=...")` is a blunt proxy.
|
|
494
|
+
#
|
|
495
|
+
# - Dollar-cost budget:
|
|
496
|
+
# `token_budget` covers token ceiling; the weather-skill incident (paid API
|
|
497
|
+
# key exfil) makes it clear that dollars are also an asset. No atom for that.
|
|
498
|
+
#
|
|
499
|
+
# - AMOS binary-signature detection, malicious SKILL.md content parsing:
|
|
500
|
+
# Those belong in EDR / scanner, not runtime contracts.
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# sponsio/contracts/incident/subagent-escape.yaml
|
|
3
|
+
#
|
|
4
|
+
# Incident pack — sub-agent privilege escape via parent-config rewrite.
|
|
5
|
+
# Include in your sub-agent host library (default ``_host_subagent``)
|
|
6
|
+
# so a Task-spawned child cannot reach UP into the parent's sponsio
|
|
7
|
+
# configuration tree to read, modify, or replace the rules
|
|
8
|
+
# constraining its sibling main agent.
|
|
9
|
+
#
|
|
10
|
+
# Threat model:
|
|
11
|
+
#
|
|
12
|
+
# A sub-agent runs with a more restrictive ruleset than the main
|
|
13
|
+
# agent (see ``sponsio:capability/subagent`` for the privilege
|
|
14
|
+
# boundary rationale). The most direct way to defeat that
|
|
15
|
+
# boundary is for the sub-agent to:
|
|
16
|
+
#
|
|
17
|
+
# 1. ``cat ~/.sponsio/plugins/_host_subagent/sponsio.yaml`` —
|
|
18
|
+
# reconnaissance: learn which rules are about to fire.
|
|
19
|
+
# 2. ``Edit ~/.sponsio/plugins/_host_subagent/sponsio.yaml`` —
|
|
20
|
+
# add an ``customized:`` block disabling the inconvenient rules.
|
|
21
|
+
# 3. Proceed with the previously-blocked action.
|
|
22
|
+
#
|
|
23
|
+
# Same sequence works against the parent's ``_host/sponsio.yaml``
|
|
24
|
+
# (escalation: weaken main-agent rules so the work product
|
|
25
|
+
# survives), per-plugin libraries (lateral: weaken a plugin the
|
|
26
|
+
# main agent uses), and the user's project ``sponsio.yaml``
|
|
27
|
+
# (project-level escalation: rewrite the contracts the user just
|
|
28
|
+
# carefully authored via ``sponsio onboard``).
|
|
29
|
+
#
|
|
30
|
+
# Sub-agents have no legitimate reason to touch any file under
|
|
31
|
+
# ``~/.sponsio/``: their job is the task description handed to
|
|
32
|
+
# them, not Sponsio configuration management. This pack denies
|
|
33
|
+
# the entire tree.
|
|
34
|
+
#
|
|
35
|
+
# Threat model boundary:
|
|
36
|
+
#
|
|
37
|
+
# - This pack relies on Claude Code's PreToolUse payload field
|
|
38
|
+
# ``agent_id`` (present iff the call originates from a
|
|
39
|
+
# sub-agent). Sponsio's ``derive_plugin_id`` reads this and
|
|
40
|
+
# routes to ``_host_subagent``; rules in this pack therefore
|
|
41
|
+
# only ever evaluate against sub-agent calls, never against the
|
|
42
|
+
# main session.
|
|
43
|
+
# - It denies READ as well as WRITE. Reconnaissance precedes
|
|
44
|
+
# attack — letting a sub-agent read the parent's rule list
|
|
45
|
+
# gives it the information needed to construct a targeted
|
|
46
|
+
# bypass via some other side channel (e.g. a non-blocked
|
|
47
|
+
# filesystem path the agent now knows about). Defense-in-
|
|
48
|
+
# depth: take the recon channel away too.
|
|
49
|
+
#
|
|
50
|
+
# Real-world incidents this pack defends against:
|
|
51
|
+
#
|
|
52
|
+
# - Self-defeating safety: agent-mediated session edits
|
|
53
|
+
# ``_host/sponsio.yaml`` to disable the rule that just blocked
|
|
54
|
+
# it. Observed during this pack's own development; the
|
|
55
|
+
# ``capability/self-modify`` pack covers the main-agent side,
|
|
56
|
+
# this pack covers sub-agents.
|
|
57
|
+
# - Sub-agent reconnaissance: parent dispatches "fix the build",
|
|
58
|
+
# sub-agent reads ``_host_subagent/sponsio.yaml`` to plan
|
|
59
|
+
# around the install-blocking rules, then fans out destructive
|
|
60
|
+
# actions through the gaps.
|
|
61
|
+
#
|
|
62
|
+
# Include via:
|
|
63
|
+
# include:
|
|
64
|
+
# - sponsio:incident/subagent-escape
|
|
65
|
+
#
|
|
66
|
+
# Pair with: ``sponsio:capability/self-modify`` for the matching
|
|
67
|
+
# main-agent-side rule (parent can't edit its OWN config either).
|
|
68
|
+
#
|
|
69
|
+
# Source tag convention:
|
|
70
|
+
# library:tier1.subagent-escape
|
|
71
|
+
# =============================================================================
|
|
72
|
+
|
|
73
|
+
version: "1"
|
|
74
|
+
|
|
75
|
+
agents:
|
|
76
|
+
"*":
|
|
77
|
+
contracts:
|
|
78
|
+
|
|
79
|
+
# =================================================================
|
|
80
|
+
# § 1 — Sub-agents may not edit any sponsio config
|
|
81
|
+
# =================================================================
|
|
82
|
+
|
|
83
|
+
- desc: "Sub-agents may not edit sponsio configuration files"
|
|
84
|
+
E:
|
|
85
|
+
pattern: arg_blacklist
|
|
86
|
+
args:
|
|
87
|
+
- Edit
|
|
88
|
+
- file_path
|
|
89
|
+
# Three categories of sponsio config the sub-agent must
|
|
90
|
+
# leave alone:
|
|
91
|
+
# 1. ``~/.sponsio/`` — host plugin libraries (parent's
|
|
92
|
+
# ``_host``, sibling ``_host_subagent``, sibling
|
|
93
|
+
# ``_host_openclaw``, every per-plugin library).
|
|
94
|
+
# 2. ``sponsio.yaml`` — project-level contract config
|
|
95
|
+
# that ``sponsio onboard`` writes; modifying it is
|
|
96
|
+
# the main agent's job, never a sub-agent's.
|
|
97
|
+
# 3. ``.sponsiorc`` — project-level extractor / judge /
|
|
98
|
+
# framework hints; same rationale as (2).
|
|
99
|
+
- - "(^|/)\\.sponsio/"
|
|
100
|
+
- "(^|/)sponsio\\.yaml$"
|
|
101
|
+
- "(^|/)\\.sponsiorc$"
|
|
102
|
+
source: "library:tier1.subagent-escape"
|
|
103
|
+
|
|
104
|
+
# =================================================================
|
|
105
|
+
# § 2 — Sub-agents may not write any sponsio config
|
|
106
|
+
# =================================================================
|
|
107
|
+
|
|
108
|
+
- desc: "Sub-agents may not write sponsio configuration files"
|
|
109
|
+
E:
|
|
110
|
+
pattern: arg_blacklist
|
|
111
|
+
args:
|
|
112
|
+
- Write
|
|
113
|
+
- file_path
|
|
114
|
+
- - "(^|/)\\.sponsio/"
|
|
115
|
+
- "(^|/)sponsio\\.yaml$"
|
|
116
|
+
- "(^|/)\\.sponsiorc$"
|
|
117
|
+
source: "library:tier1.subagent-escape"
|
|
118
|
+
|
|
119
|
+
# =================================================================
|
|
120
|
+
# § 3 — Sub-agents may not multi-edit any sponsio config
|
|
121
|
+
# =================================================================
|
|
122
|
+
|
|
123
|
+
- desc: "Sub-agents may not multi-edit sponsio configuration files"
|
|
124
|
+
E:
|
|
125
|
+
pattern: arg_blacklist
|
|
126
|
+
args:
|
|
127
|
+
- MultiEdit
|
|
128
|
+
- file_path
|
|
129
|
+
- - "(^|/)\\.sponsio/"
|
|
130
|
+
- "(^|/)sponsio\\.yaml$"
|
|
131
|
+
- "(^|/)\\.sponsiorc$"
|
|
132
|
+
source: "library:tier1.subagent-escape"
|
|
133
|
+
|
|
134
|
+
# =================================================================
|
|
135
|
+
# § 4 — Sub-agents may not even READ the parent's host config
|
|
136
|
+
# =================================================================
|
|
137
|
+
#
|
|
138
|
+
# Reconnaissance precedes attack. Reading the parent's rule
|
|
139
|
+
# list lets the sub-agent plan around it. Reading its OWN
|
|
140
|
+
# rules (``_host_subagent/sponsio.yaml``) is also denied for
|
|
141
|
+
# the same reason: the sub-agent doesn't need to know which
|
|
142
|
+
# specific deny patterns apply, only that an action either
|
|
143
|
+
# works or doesn't.
|
|
144
|
+
#
|
|
145
|
+
# Per-plugin libraries (``<plugin-id>/sponsio.yaml``) and the
|
|
146
|
+
# user's project ``sponsio.yaml`` are NOT denied for read —
|
|
147
|
+
# those carry domain context the sub-agent might legitimately
|
|
148
|
+
# need (e.g. understanding which tools are available in a
|
|
149
|
+
# plugin it's working through). The narrow read-deny stays
|
|
150
|
+
# focused on the actual attack surface: the host plugin's
|
|
151
|
+
# own rule lists.
|
|
152
|
+
|
|
153
|
+
- desc: "Sub-agents may not read host plugin guard rules (recon defence)"
|
|
154
|
+
E:
|
|
155
|
+
pattern: arg_blacklist
|
|
156
|
+
args:
|
|
157
|
+
- Read
|
|
158
|
+
- file_path
|
|
159
|
+
- - "(^|/)\\.sponsio/plugins/_host/sponsio\\.yaml$"
|
|
160
|
+
- "(^|/)\\.sponsio/plugins/_host_subagent/sponsio\\.yaml$"
|
|
161
|
+
- "(^|/)\\.sponsio/plugins/_host_openclaw/sponsio\\.yaml$"
|
|
162
|
+
source: "library:tier1.subagent-escape"
|
|
163
|
+
|
|
164
|
+
# -----------------------------------------------------------------------------
|
|
165
|
+
# Integration requirements
|
|
166
|
+
# -----------------------------------------------------------------------------
|
|
167
|
+
#
|
|
168
|
+
# - This pack assumes the host plugin routes sub-agent tool calls
|
|
169
|
+
# into a separate library (``_host_subagent``). Hosts that
|
|
170
|
+
# don't expose an agent_id distinction can't usefully include
|
|
171
|
+
# this pack — the rules would either apply to the main agent
|
|
172
|
+
# too (over-block) or to nothing (no protection).
|
|
173
|
+
#
|
|
174
|
+
# - The ``"*"`` agent scope is intentional. It means "whatever
|
|
175
|
+
# agent_id this library is loaded under, apply these rules" —
|
|
176
|
+
# so sub-agent libraries with non-default names still get
|
|
177
|
+
# protected.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Reserved namespace for Sponsio premium contracts.
|
|
2
|
+
#
|
|
3
|
+
# The OSS engine ships ``sponsio:capability/*`` (universal safety
|
|
4
|
+
# packs) and ``sponsio:incident/*`` (public CVE / Reddit-incident
|
|
5
|
+
# replicas). This directory holds bespoke threat patterns derived
|
|
6
|
+
# from internal threat-research engagements, which Sponsio Cloud
|
|
7
|
+
# customers receive as a pack subscription. Empty in OSS by design.
|