@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.
Files changed (113) hide show
  1. package/contracts/capability/credentials.yaml +188 -0
  2. package/contracts/capability/database.yaml +141 -0
  3. package/contracts/capability/filesystem-strict.yaml +101 -0
  4. package/contracts/capability/filesystem.yaml +239 -0
  5. package/contracts/capability/self-modify.yaml +152 -0
  6. package/contracts/capability/shell.yaml +246 -0
  7. package/contracts/capability/subagent.yaml +184 -0
  8. package/contracts/core/llm_safety.yaml +130 -0
  9. package/contracts/core/runaway.yaml +43 -0
  10. package/contracts/core/universal.yaml +34 -0
  11. package/contracts/incident/claude-code-secret-bypass.yaml +197 -0
  12. package/contracts/incident/cursor-railway-wipe.yaml +164 -0
  13. package/contracts/incident/mcp-composition.yaml +418 -0
  14. package/contracts/incident/openclaw.yaml +500 -0
  15. package/contracts/incident/subagent-escape.yaml +177 -0
  16. package/contracts/premium/.gitkeep +7 -0
  17. package/dist/contract.d.ts +59 -0
  18. package/dist/contract.d.ts.map +1 -0
  19. package/dist/contract.js +97 -0
  20. package/dist/contract.js.map +1 -0
  21. package/dist/core/config-loader.d.ts +90 -0
  22. package/dist/core/config-loader.d.ts.map +1 -0
  23. package/dist/core/config-loader.js +530 -0
  24. package/dist/core/config-loader.js.map +1 -0
  25. package/dist/core/evaluator.d.ts +15 -0
  26. package/dist/core/evaluator.d.ts.map +1 -0
  27. package/dist/core/evaluator.js +87 -0
  28. package/dist/core/evaluator.js.map +1 -0
  29. package/dist/core/formula.d.ts +103 -0
  30. package/dist/core/formula.d.ts.map +1 -0
  31. package/dist/core/formula.js +167 -0
  32. package/dist/core/formula.js.map +1 -0
  33. package/dist/core/grounding.d.ts +43 -0
  34. package/dist/core/grounding.d.ts.map +1 -0
  35. package/dist/core/grounding.js +269 -0
  36. package/dist/core/grounding.js.map +1 -0
  37. package/dist/core/nl-parser.d.ts +12 -0
  38. package/dist/core/nl-parser.d.ts.map +1 -0
  39. package/dist/core/nl-parser.js +152 -0
  40. package/dist/core/nl-parser.js.map +1 -0
  41. package/dist/core/pack-loader.d.ts +15 -0
  42. package/dist/core/pack-loader.d.ts.map +1 -0
  43. package/dist/core/pack-loader.js +220 -0
  44. package/dist/core/pack-loader.js.map +1 -0
  45. package/dist/core/parser.d.ts +19 -0
  46. package/dist/core/parser.d.ts.map +1 -0
  47. package/dist/core/parser.js +235 -0
  48. package/dist/core/parser.js.map +1 -0
  49. package/dist/core/pattern-factory.d.ts +38 -0
  50. package/dist/core/pattern-factory.d.ts.map +1 -0
  51. package/dist/core/pattern-factory.js +222 -0
  52. package/dist/core/pattern-factory.js.map +1 -0
  53. package/dist/core/patterns.d.ts +67 -0
  54. package/dist/core/patterns.d.ts.map +1 -0
  55. package/dist/core/patterns.js +585 -0
  56. package/dist/core/patterns.js.map +1 -0
  57. package/dist/core/session-log.d.ts +91 -0
  58. package/dist/core/session-log.d.ts.map +1 -0
  59. package/dist/core/session-log.js +196 -0
  60. package/dist/core/session-log.js.map +1 -0
  61. package/dist/core/spans.d.ts +92 -0
  62. package/dist/core/spans.d.ts.map +1 -0
  63. package/dist/core/spans.js +148 -0
  64. package/dist/core/spans.js.map +1 -0
  65. package/dist/core/sto.d.ts +120 -0
  66. package/dist/core/sto.d.ts.map +1 -0
  67. package/dist/core/sto.js +147 -0
  68. package/dist/core/sto.js.map +1 -0
  69. package/dist/index.d.ts +267 -0
  70. package/dist/index.d.ts.map +1 -0
  71. package/dist/index.js +540 -0
  72. package/dist/index.js.map +1 -0
  73. package/dist/integrations/claude-agent.d.ts +29 -0
  74. package/dist/integrations/claude-agent.d.ts.map +1 -0
  75. package/dist/integrations/claude-agent.js +40 -0
  76. package/dist/integrations/claude-agent.js.map +1 -0
  77. package/dist/integrations/google-adk.d.ts +27 -0
  78. package/dist/integrations/google-adk.d.ts.map +1 -0
  79. package/dist/integrations/google-adk.js +81 -0
  80. package/dist/integrations/google-adk.js.map +1 -0
  81. package/dist/integrations/langchain.d.ts +26 -0
  82. package/dist/integrations/langchain.d.ts.map +1 -0
  83. package/dist/integrations/langchain.js +74 -0
  84. package/dist/integrations/langchain.js.map +1 -0
  85. package/dist/integrations/openai-agents.d.ts +45 -0
  86. package/dist/integrations/openai-agents.d.ts.map +1 -0
  87. package/dist/integrations/openai-agents.js +81 -0
  88. package/dist/integrations/openai-agents.js.map +1 -0
  89. package/dist/integrations/openai.d.ts +24 -0
  90. package/dist/integrations/openai.d.ts.map +1 -0
  91. package/dist/integrations/openai.js +77 -0
  92. package/dist/integrations/openai.js.map +1 -0
  93. package/dist/integrations/vercel-ai.d.ts +38 -0
  94. package/dist/integrations/vercel-ai.d.ts.map +1 -0
  95. package/dist/integrations/vercel-ai.js +107 -0
  96. package/dist/integrations/vercel-ai.js.map +1 -0
  97. package/dist/render/components.d.ts +23 -0
  98. package/dist/render/components.d.ts.map +1 -0
  99. package/dist/render/components.js +133 -0
  100. package/dist/render/components.js.map +1 -0
  101. package/dist/render/derive.d.ts +23 -0
  102. package/dist/render/derive.d.ts.map +1 -0
  103. package/dist/render/derive.js +92 -0
  104. package/dist/render/derive.js.map +1 -0
  105. package/dist/render/session-view.d.ts +19 -0
  106. package/dist/render/session-view.d.ts.map +1 -0
  107. package/dist/render/session-view.js +236 -0
  108. package/dist/render/session-view.js.map +1 -0
  109. package/dist/render/tokens.d.ts +44 -0
  110. package/dist/render/tokens.d.ts.map +1 -0
  111. package/dist/render/tokens.js +72 -0
  112. package/dist/render/tokens.js.map +1 -0
  113. 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.