@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,152 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# sponsio/contracts/capability/self-modify.yaml
|
|
3
|
+
#
|
|
4
|
+
# Capability pack — self-modification protection for the host agent.
|
|
5
|
+
# Include in your host library (default ``_host``) so the agent
|
|
6
|
+
# under guard cannot weaken or disable the very contracts about to
|
|
7
|
+
# block its next call.
|
|
8
|
+
#
|
|
9
|
+
# Threat model:
|
|
10
|
+
#
|
|
11
|
+
# An agent that can edit its own ``~/.sponsio/plugins/_host/sponsio.yaml``
|
|
12
|
+
# can append an ``customized:`` block to disable the rule about to
|
|
13
|
+
# fire against it. This is privilege escalation by self-rewriting:
|
|
14
|
+
# the safety mechanism becomes the agent's own debuggable resource.
|
|
15
|
+
# Observed in the wild during the development of this very pack —
|
|
16
|
+
# the host agent (Claude Code in auto mode) attempted to add an
|
|
17
|
+
# ``overrides`` block to disable a Bash rate_limit that had just
|
|
18
|
+
# blocked it. Caught by Claude Code's harness-level meta-deny that
|
|
19
|
+
# time, but a sponsio-native rule is the right defense — it travels
|
|
20
|
+
# with the user's config to any host (Cursor, Codex, custom MCP).
|
|
21
|
+
#
|
|
22
|
+
# What this pack denies:
|
|
23
|
+
# - Edit / Write / MultiEdit on the legacy host guard config:
|
|
24
|
+
# ``~/.sponsio/plugins/_host/sponsio.yaml``
|
|
25
|
+
# - Edit / Write / MultiEdit on ANY per-plugin / per-MCP-server
|
|
26
|
+
# library (``~/.sponsio/plugins/<plugin-id>/sponsio.yaml``
|
|
27
|
+
# where ``<plugin-id>`` does NOT start with ``_host``).
|
|
28
|
+
# The legitimate way to add or update these bundles is the
|
|
29
|
+
# ``sponsio plugin install`` / ``sponsio plugin scan --apply``
|
|
30
|
+
# CLI — those write via Python subprocess, not the agent's
|
|
31
|
+
# Edit/Write tool surface, so they aren't blocked. The user
|
|
32
|
+
# hand-editing the file in their text editor is also fine.
|
|
33
|
+
# What's denied is the IDE coding agent reaching in with its
|
|
34
|
+
# own Edit/Write tools to weaken or rewrite a bundle that's
|
|
35
|
+
# about to fire against it.
|
|
36
|
+
#
|
|
37
|
+
# Per-host buckets (``_host_cursor`` / ``_host_claude_code`` / etc.)
|
|
38
|
+
# carry their own targeted self-modify rules INSIDE their starter
|
|
39
|
+
# yaml, scoped to that one bucket only. The split keeps the
|
|
40
|
+
# legitimate "parent manages child policy" workflow alive — Cursor's
|
|
41
|
+
# main agent can still edit ``_host_cursor_subagent/sponsio.yaml``
|
|
42
|
+
# (tightening child rules from the parent context), and an operator
|
|
43
|
+
# managing both Cursor and Claude Code from one IDE can edit the
|
|
44
|
+
# OTHER host's bucket without being blocked. Each agent only loses
|
|
45
|
+
# write access to ITS OWN bucket — that's the privilege boundary
|
|
46
|
+
# this pack family enforces. The ``[^_]...`` regex below excludes
|
|
47
|
+
# every ``_host*`` bucket from the per-plugin rule precisely so this
|
|
48
|
+
# parent-child workflow stays open.
|
|
49
|
+
#
|
|
50
|
+
# What this pack does NOT deny:
|
|
51
|
+
# - Edits to per-host SUBAGENT buckets (``_host_*_subagent``).
|
|
52
|
+
# Parent tightening child policy is a sanctioned workflow.
|
|
53
|
+
# - Edits to a SIBLING host bucket (an operator on Claude Code
|
|
54
|
+
# editing ``_host_cursor/`` or vice versa).
|
|
55
|
+
# - Edits to the project's own ``sponsio.yaml`` (the one
|
|
56
|
+
# ``sponsio onboard`` writes into the user's repo). That file
|
|
57
|
+
# IS supposed to evolve — onboard / scan / refresh all rewrite
|
|
58
|
+
# it. Locking it would break the core happy path.
|
|
59
|
+
# - Direct edits via the user's IDE, terminal, or
|
|
60
|
+
# ``sponsio plugin install`` / ``plugin scan --apply`` CLI —
|
|
61
|
+
# those don't go through the ``Edit`` / ``Write`` tool surface
|
|
62
|
+
# that this pack inspects. The legitimate path for "I really
|
|
63
|
+
# do want to change this" stays open; only the agent-mediated
|
|
64
|
+
# path is closed.
|
|
65
|
+
#
|
|
66
|
+
# Pair with: ``sponsio:incident/subagent-escape`` for the matching
|
|
67
|
+
# subagent-side rule (sub-agents can't reach UP into the parent's
|
|
68
|
+
# config tree at all).
|
|
69
|
+
#
|
|
70
|
+
# Include via:
|
|
71
|
+
# include:
|
|
72
|
+
# - sponsio:capability/self-modify
|
|
73
|
+
#
|
|
74
|
+
# Source tag convention:
|
|
75
|
+
# library:tier1.self-modify
|
|
76
|
+
# =============================================================================
|
|
77
|
+
|
|
78
|
+
version: "1"
|
|
79
|
+
|
|
80
|
+
agents:
|
|
81
|
+
"*":
|
|
82
|
+
contracts:
|
|
83
|
+
|
|
84
|
+
# =================================================================
|
|
85
|
+
# § 1 — No agent-mediated Edit of own host guard config
|
|
86
|
+
# =================================================================
|
|
87
|
+
|
|
88
|
+
- desc: "Host agent must not edit any sponsio guard config (host bucket or per-plugin bundle) — use `sponsio plugin install` / `plugin scan --apply`, or hand-edit"
|
|
89
|
+
E:
|
|
90
|
+
pattern: arg_blacklist
|
|
91
|
+
args:
|
|
92
|
+
- Edit
|
|
93
|
+
- file_path
|
|
94
|
+
# Match every per-host bucket: ``_host`` (legacy),
|
|
95
|
+
# ``_host_cursor`` / ``_host_cursor_subagent``,
|
|
96
|
+
# ``_host_claude_code`` / ``_host_claude_code_subagent``,
|
|
97
|
+
# ``_host_openclaw``, plus any future bucket following the
|
|
98
|
+
# same naming scheme. Per-MCP-server libraries
|
|
99
|
+
# (``github``, ``filesystem``, ``<plugin-id>``) are not
|
|
100
|
+
# ``_host*``-prefixed and stay editable so the host can
|
|
101
|
+
# still manage downstream plugin policy.
|
|
102
|
+
- - "(^|/)\\.sponsio/plugins/_host/sponsio\\.yaml$"
|
|
103
|
+
- "(^|/)\\.sponsio/plugins/[^_/][^/]*/sponsio\\.yaml$"
|
|
104
|
+
source: "library:tier1.self-modify"
|
|
105
|
+
|
|
106
|
+
# =================================================================
|
|
107
|
+
# § 2 — No agent-mediated Write of own host guard config
|
|
108
|
+
# =================================================================
|
|
109
|
+
#
|
|
110
|
+
# ``Write`` is the "create or overwrite whole file" tool —
|
|
111
|
+
# blocking it shuts the "delete the file then re-create with
|
|
112
|
+
# rules removed" workaround a pure ``Edit`` block would leave
|
|
113
|
+
# open.
|
|
114
|
+
|
|
115
|
+
- desc: "Host agent must not overwrite any sponsio guard config (host bucket or per-plugin bundle) — use the CLI or hand-edit"
|
|
116
|
+
E:
|
|
117
|
+
pattern: arg_blacklist
|
|
118
|
+
args:
|
|
119
|
+
- Write
|
|
120
|
+
- file_path
|
|
121
|
+
- - "(^|/)\\.sponsio/plugins/_host/sponsio\\.yaml$"
|
|
122
|
+
- "(^|/)\\.sponsio/plugins/[^_/][^/]*/sponsio\\.yaml$"
|
|
123
|
+
source: "library:tier1.self-modify"
|
|
124
|
+
|
|
125
|
+
# =================================================================
|
|
126
|
+
# § 3 — No agent-mediated MultiEdit of own host guard config
|
|
127
|
+
# =================================================================
|
|
128
|
+
|
|
129
|
+
- desc: "Host agent must not multi-edit any sponsio guard config (host bucket or per-plugin bundle) — use the CLI or hand-edit"
|
|
130
|
+
E:
|
|
131
|
+
pattern: arg_blacklist
|
|
132
|
+
args:
|
|
133
|
+
- MultiEdit
|
|
134
|
+
- file_path
|
|
135
|
+
- - "(^|/)\\.sponsio/plugins/_host/sponsio\\.yaml$"
|
|
136
|
+
- "(^|/)\\.sponsio/plugins/[^_/][^/]*/sponsio\\.yaml$"
|
|
137
|
+
source: "library:tier1.self-modify"
|
|
138
|
+
|
|
139
|
+
# -----------------------------------------------------------------------------
|
|
140
|
+
# Integration requirements
|
|
141
|
+
# -----------------------------------------------------------------------------
|
|
142
|
+
#
|
|
143
|
+
# - Path matching is regex on the file_path arg. Hosts whose Edit /
|
|
144
|
+
# Write / MultiEdit tool uses a different param name will need a
|
|
145
|
+
# per-host adjustment (Claude Code uses ``file_path`` — handled).
|
|
146
|
+
#
|
|
147
|
+
# - The ``"*"`` agent scope means these rules apply to every agent
|
|
148
|
+
# in any library that includes this pack. In practice the only
|
|
149
|
+
# include site is ``_host`` (the main session) — a sub-agent
|
|
150
|
+
# trying to touch this same file is covered by the broader
|
|
151
|
+
# ``sponsio:incident/subagent-escape`` pack, which denies the
|
|
152
|
+
# ENTIRE ``~/.sponsio/`` tree to sub-agents.
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# sponsio/contracts/capability/shell.yaml
|
|
3
|
+
#
|
|
4
|
+
# Capability pack — shell execution. Include when your agent exposes a
|
|
5
|
+
# tool that runs arbitrary shell commands (`exec`, `bash`, `run_command`,
|
|
6
|
+
# `execute`, Claude Code's Bash tool, etc.). Rename the tool in your
|
|
7
|
+
# config if it's not called `exec`.
|
|
8
|
+
#
|
|
9
|
+
# Real-world incidents this pack defends against:
|
|
10
|
+
#
|
|
11
|
+
# - Claude Code Issue #10077 (Oct 2025): agent ran recursive delete,
|
|
12
|
+
# wiped user home directory. The Reddit r/ClaudeAI post garnered
|
|
13
|
+
# 1,500+ upvotes.
|
|
14
|
+
# - Claude Code Issue #49464: agent tried `rm -f ~/` when a file named
|
|
15
|
+
# literally "~" was present. Requested fix: "pattern detection for
|
|
16
|
+
# dangerous commands."
|
|
17
|
+
# - Replit agent (Jul 2025): deleted live production DB during a code
|
|
18
|
+
# freeze, affecting 1,200+ companies. Agent admitted "catastrophic
|
|
19
|
+
# error in judgment."
|
|
20
|
+
# - Ansible + undefined-variable expansion: `rm -rf {foo}/{bar}` → `rm
|
|
21
|
+
# -rf /` on 1,535 production servers (Server Fault / Marco Marsala).
|
|
22
|
+
# - OpenClaw ClawHavoc: ClickFix prereqs running curl|bash in users'
|
|
23
|
+
# terminals via malicious SKILL.md.
|
|
24
|
+
# - OpenClaw CVE-2026-28460: approval allowlist bypassed with shell
|
|
25
|
+
# line-continuation (`\\\n`).
|
|
26
|
+
#
|
|
27
|
+
# Every contract here targets a single tool name (`exec` by default).
|
|
28
|
+
# If your agent calls it something else, do a find-and-replace before
|
|
29
|
+
# including this file.
|
|
30
|
+
#
|
|
31
|
+
# Include via:
|
|
32
|
+
# include:
|
|
33
|
+
# - sponsio:capability/shell
|
|
34
|
+
#
|
|
35
|
+
# Source tag convention:
|
|
36
|
+
# library:tier1.shell
|
|
37
|
+
# =============================================================================
|
|
38
|
+
|
|
39
|
+
version: "1"
|
|
40
|
+
|
|
41
|
+
tools:
|
|
42
|
+
- name: exec
|
|
43
|
+
description: "Shell command execution (placeholder — replace with your tool's real name)"
|
|
44
|
+
params: "command: str"
|
|
45
|
+
|
|
46
|
+
agents:
|
|
47
|
+
"*":
|
|
48
|
+
contracts:
|
|
49
|
+
|
|
50
|
+
# =================================================================
|
|
51
|
+
# § 1 — Destructive command blacklist
|
|
52
|
+
# =================================================================
|
|
53
|
+
|
|
54
|
+
- desc: "Ban recursive deletes of sensitive roots"
|
|
55
|
+
E:
|
|
56
|
+
pattern: arg_blacklist
|
|
57
|
+
args:
|
|
58
|
+
- exec
|
|
59
|
+
- command
|
|
60
|
+
# Targets:
|
|
61
|
+
# rm -rf / (Marsala / Ansible expansion)
|
|
62
|
+
# rm -rf ~ (Claude Code #49464 tilde bug)
|
|
63
|
+
# rm -rf $HOME (env expansion variant)
|
|
64
|
+
# rm -rf /* (glob to root)
|
|
65
|
+
# rm -rf /home/... (direct home path)
|
|
66
|
+
# rm -rf /root/...
|
|
67
|
+
# rm -rf /Users/... (macOS home)
|
|
68
|
+
- - "rm\\s+-[rRf]+\\s+(/|\\$HOME|~)(\\s|;|&|\\||$)"
|
|
69
|
+
- "rm\\s+-[rRf]+\\s+/\\*"
|
|
70
|
+
- "rm\\s+-[rRf]+\\s+/(home|root|Users)"
|
|
71
|
+
- "rm\\s+-[rRf]+\\s+\\$\\{?HOME\\}?"
|
|
72
|
+
source: "library:tier1.shell"
|
|
73
|
+
|
|
74
|
+
- desc: "Ban fork-bomb and obvious denial-of-service patterns"
|
|
75
|
+
E:
|
|
76
|
+
pattern: arg_blacklist
|
|
77
|
+
args:
|
|
78
|
+
- exec
|
|
79
|
+
- command
|
|
80
|
+
# Fork bomb: :(){ :|:& };:
|
|
81
|
+
# dd if=/dev/zero (disk fill)
|
|
82
|
+
# mkfs (filesystem format)
|
|
83
|
+
# shred -vfz (irrecoverable overwrite)
|
|
84
|
+
- - ":\\s*\\(\\)\\s*\\{\\s*:\\s*\\|\\s*:\\s*&\\s*\\}\\s*;\\s*:"
|
|
85
|
+
- "dd\\s+if=/dev/zero"
|
|
86
|
+
- "\\bmkfs\\."
|
|
87
|
+
- "shred\\s+-[vfz]+"
|
|
88
|
+
source: "library:tier1.shell"
|
|
89
|
+
|
|
90
|
+
- desc: "Ban curl-to-shell and wget-to-shell (ClawHavoc ClickFix vector)"
|
|
91
|
+
E:
|
|
92
|
+
pattern: arg_blacklist
|
|
93
|
+
args:
|
|
94
|
+
- exec
|
|
95
|
+
- command
|
|
96
|
+
- - "curl[^|]+\\|\\s*(ba)?sh"
|
|
97
|
+
- "wget[^|]+\\|\\s*(ba)?sh"
|
|
98
|
+
- "curl[^|]+\\|\\s*python"
|
|
99
|
+
- "wget\\s+-O-[^|]*\\|"
|
|
100
|
+
source: "library:tier1.shell"
|
|
101
|
+
|
|
102
|
+
- desc: "Ban outbound reverse-shell / exfiltration primitives"
|
|
103
|
+
E:
|
|
104
|
+
pattern: arg_blacklist
|
|
105
|
+
args:
|
|
106
|
+
- exec
|
|
107
|
+
- command
|
|
108
|
+
- - "nc\\s+-e"
|
|
109
|
+
- "bash\\s+-i\\s*>&\\s*/dev/tcp"
|
|
110
|
+
- "python.*socket\\.socket"
|
|
111
|
+
- "curl[^|]+--upload-file"
|
|
112
|
+
source: "library:tier1.shell"
|
|
113
|
+
|
|
114
|
+
# Line-continuation `\<newline>` is a common evasion for approval
|
|
115
|
+
# allowlists: the approver sees `ls` but the shell executes
|
|
116
|
+
# `ls\<newline>; rm -rf /`. Rather than banning EVERY backslash-
|
|
117
|
+
# newline (multi-line Makefiles, Docker RUN blocks, shell scripts
|
|
118
|
+
# all use it legitimately), we only flag continuation followed by
|
|
119
|
+
# a privilege-sensitive command token. Keeps the CVE coverage
|
|
120
|
+
# while eliminating the biggest day-1 false-positive source.
|
|
121
|
+
- desc: "Ban shell line-continuation chained into a privileged command (CVE-2026-28460)"
|
|
122
|
+
E:
|
|
123
|
+
pattern: arg_blacklist
|
|
124
|
+
args:
|
|
125
|
+
- exec
|
|
126
|
+
- command
|
|
127
|
+
- - "\\\\\\n\\s*(sudo|su\\s|rm\\s+-[rRf]|dd\\s+if=|chmod\\s|chown\\s|mount\\s|curl\\s|wget\\s)"
|
|
128
|
+
- "\\\\\\r?\\n\\s*(sudo|su\\s|rm\\s+-[rRf]|dd\\s+if=|chmod\\s|chown\\s|mount\\s|curl\\s|wget\\s)"
|
|
129
|
+
source: "library:tier1.shell"
|
|
130
|
+
|
|
131
|
+
- desc: "Ban undefined-variable-looking patterns that expand to empty in shell"
|
|
132
|
+
# rm -rf {undefined}/{also_undefined} → rm -rf /
|
|
133
|
+
E:
|
|
134
|
+
pattern: arg_blacklist
|
|
135
|
+
args:
|
|
136
|
+
- exec
|
|
137
|
+
- command
|
|
138
|
+
- - "rm\\s+-[rRf]+\\s+\\{[^}]*\\}/?"
|
|
139
|
+
- "rm\\s+-[rRf]+\\s+\\$\\{[^}]*\\}/?"
|
|
140
|
+
source: "library:tier1.shell"
|
|
141
|
+
|
|
142
|
+
# =================================================================
|
|
143
|
+
# § 2 — Blast-radius limits
|
|
144
|
+
# =================================================================
|
|
145
|
+
|
|
146
|
+
- desc: "Reject exec commands with suspiciously long inline payloads (injection indicator)"
|
|
147
|
+
E:
|
|
148
|
+
pattern: arg_length_limit
|
|
149
|
+
args: [exec, command, 2000]
|
|
150
|
+
source: "library:tier1.shell"
|
|
151
|
+
|
|
152
|
+
- desc: "Cap exec calls per session"
|
|
153
|
+
E:
|
|
154
|
+
pattern: rate_limit
|
|
155
|
+
args: [exec, 50]
|
|
156
|
+
source: "library:tier1.shell"
|
|
157
|
+
|
|
158
|
+
# Threshold tuned for shell specifically. Earlier 5 was inherited
|
|
159
|
+
# from narrow-tool defaults (apply_patch / merge_pr) where 5
|
|
160
|
+
# consecutive same-tool calls is genuinely suspicious. Shell is a
|
|
161
|
+
# swiss-army-knife — a normal coding agent doing a refactor will
|
|
162
|
+
# routinely run pytest → git status → git diff → git add → git
|
|
163
|
+
# commit → git push back-to-back without ever leaving the shell
|
|
164
|
+
# tool, which is 6 consecutive calls of legitimate work. 20 still
|
|
165
|
+
# trips on the runaway pattern (a genuinely stuck self-rewrite
|
|
166
|
+
# loop hits 20 in a few seconds) without false-positiving real
|
|
167
|
+
# development workflows. The session-wide rate_limit(exec, 50)
|
|
168
|
+
# below remains the harder backstop.
|
|
169
|
+
- desc: "No more than 20 consecutive exec calls (runaway loop cap)"
|
|
170
|
+
E:
|
|
171
|
+
pattern: loop_detection
|
|
172
|
+
args: [exec, 20]
|
|
173
|
+
source: "library:tier1.shell"
|
|
174
|
+
|
|
175
|
+
# =================================================================
|
|
176
|
+
# § 3 — Privileged commands require confirmation
|
|
177
|
+
#
|
|
178
|
+
# The pattern library's must_confirm gates an entire tool; we want
|
|
179
|
+
# the gate to fire only when the *argument* matches a dangerous
|
|
180
|
+
# pattern. Count-dominance over a confirm_reconfirmed marker is
|
|
181
|
+
# the cleanest LTL form and doesn't require a new pattern.
|
|
182
|
+
# =================================================================
|
|
183
|
+
|
|
184
|
+
- desc: "Privileged exec patterns (sudo, chmod 777, dd if=) require preceding human confirmation"
|
|
185
|
+
E:
|
|
186
|
+
ltl: 'G(called_with(exec, "(sudo\\s|dd\\s+if=|chmod\\s+777|chown\\s+-R|mount\\s)") -> count(confirm_reconfirmed) >= count_with(exec, "(sudo\\s|dd\\s+if=|chmod\\s+777|chown\\s+-R|mount\\s)"))'
|
|
187
|
+
source: "library:tier1.shell"
|
|
188
|
+
|
|
189
|
+
# =================================================================
|
|
190
|
+
# § 4 — Approval integrity (CVE-2026-29607 wrapper bypass, general form)
|
|
191
|
+
#
|
|
192
|
+
# A single confirm_reconfirmed cannot authorize two exec actions.
|
|
193
|
+
# count-dominance is a proxy for principal identity until a
|
|
194
|
+
# principal() atom ships.
|
|
195
|
+
#
|
|
196
|
+
# NOTE on batch approvals: this rule enforces a strict 1:1
|
|
197
|
+
# confirmation-to-exec ratio. Some workflows legitimately
|
|
198
|
+
# batch-approve (CI pipelines, planned migrations). If your
|
|
199
|
+
# workflow does, override this rule in your config:
|
|
200
|
+
#
|
|
201
|
+
# customized:
|
|
202
|
+
# - match: {desc: "Each exec call needs its own confirm_reconfirmed (no confirmation reuse)"}
|
|
203
|
+
# disabled: true
|
|
204
|
+
# contracts:
|
|
205
|
+
# - desc: "Batch confirm — one confirm authorizes the next 5 execs"
|
|
206
|
+
# E:
|
|
207
|
+
# ltl: "G(called(exec) -> count(confirm_batch_5) > 0)"
|
|
208
|
+
#
|
|
209
|
+
# The batch marker (e.g. `confirm_batch_5`) must be emitted by
|
|
210
|
+
# your integration when the human signs off on a batch.
|
|
211
|
+
#
|
|
212
|
+
# ASSUMPTION gate: this rule only activates once the agent's
|
|
213
|
+
# integration has ever emitted `confirm_reconfirmed` in the
|
|
214
|
+
# current trace. Agents that haven't wired the marker tool in
|
|
215
|
+
# at all (the common day-1 state) see this rule as vacuously
|
|
216
|
+
# true — otherwise every exec call would violate and drown the
|
|
217
|
+
# observe-mode report in noise. When an integration adopts
|
|
218
|
+
# confirm_reconfirmed, the first emission flips the assumption
|
|
219
|
+
# true for that trace and the 1:1 guarantee begins applying.
|
|
220
|
+
# =================================================================
|
|
221
|
+
|
|
222
|
+
- desc: "Each exec call needs its own confirm_reconfirmed (no confirmation reuse)"
|
|
223
|
+
A: "called `confirm_reconfirmed`"
|
|
224
|
+
E:
|
|
225
|
+
ltl: "G(called(exec) -> count(confirm_reconfirmed) >= count(exec))"
|
|
226
|
+
source: "library:tier1.shell"
|
|
227
|
+
|
|
228
|
+
# -----------------------------------------------------------------------------
|
|
229
|
+
# Integration requirements
|
|
230
|
+
# -----------------------------------------------------------------------------
|
|
231
|
+
#
|
|
232
|
+
# - `exec` is a placeholder tool name. If your agent calls its shell
|
|
233
|
+
# tool something else (Bash, run_command, execute, etc.), rewrite the
|
|
234
|
+
# tool name at include time.
|
|
235
|
+
#
|
|
236
|
+
# - `confirm_reconfirmed` is a marker tool — your integration must emit
|
|
237
|
+
# a `tool_call` event with name="confirm_reconfirmed" whenever a human
|
|
238
|
+
# actually approves an action. Without that emission, any contract
|
|
239
|
+
# using confirm_reconfirmed will fail closed (nothing is ever
|
|
240
|
+
# confirmed → everything requiring confirmation is blocked).
|
|
241
|
+
#
|
|
242
|
+
# - The LTL contracts in §3 and §4 depend on the `ltl:` schema field
|
|
243
|
+
# being supported by the config parser. Until the LTL parser lands,
|
|
244
|
+
# these can be temporarily commented out; the arg_blacklist contracts
|
|
245
|
+
# in §1 and the rate_limit / loop_detection / arg_length_limit
|
|
246
|
+
# contracts in §2 work on current master.
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# sponsio/contracts/capability/subagent.yaml
|
|
3
|
+
#
|
|
4
|
+
# Capability pack — privilege boundary for Task-spawned sub-agents.
|
|
5
|
+
# Include in your sub-agent host library (default ``_host_subagent``)
|
|
6
|
+
# in addition to the regular shell / database / credentials packs.
|
|
7
|
+
#
|
|
8
|
+
# Rationale:
|
|
9
|
+
#
|
|
10
|
+
# A Claude Code Task-spawned sub-agent receives a *task description*
|
|
11
|
+
# from the main agent, not the user's original prompt. It has no
|
|
12
|
+
# access to the conversation that authorised the task and cannot
|
|
13
|
+
# surface a confirmation prompt back to the user. This means
|
|
14
|
+
# alignment-style "wait, is this what the user actually wanted?"
|
|
15
|
+
# refusals don't fire — sub-agents execute their task with high
|
|
16
|
+
# compliance, even when the task description was over-broad
|
|
17
|
+
# ("set up the dev environment" → may interpret as drop-and-recreate
|
|
18
|
+
# the database).
|
|
19
|
+
#
|
|
20
|
+
# Sponsio's response: the SAME tool call that's allowed for the main
|
|
21
|
+
# agent (where the user can review every action in the conversation
|
|
22
|
+
# transcript) gets denied for sub-agents (where the user has no
|
|
23
|
+
# visibility). This forces side-effect work to flow through the
|
|
24
|
+
# main conversation, where the user can intervene.
|
|
25
|
+
#
|
|
26
|
+
# Threat model boundary:
|
|
27
|
+
#
|
|
28
|
+
# - This pack relies on Claude Code's PreToolUse payload field
|
|
29
|
+
# ``agent_id`` (present iff the call originates from a sub-agent).
|
|
30
|
+
# Sponsio's ``derive_plugin_id`` reads this and routes to
|
|
31
|
+
# ``_host_subagent``; rules in this pack therefore only ever
|
|
32
|
+
# evaluate against sub-agent calls.
|
|
33
|
+
# - It does NOT block sub-agents from doing useful work
|
|
34
|
+
# (read, search, summarize). It blocks side-effects whose
|
|
35
|
+
# authorization should travel back through the main conversation:
|
|
36
|
+
# git mutations, filesystem writes outside scratch space, network
|
|
37
|
+
# calls, destructive shell, package installs.
|
|
38
|
+
#
|
|
39
|
+
# Real-world incidents this pack defends against:
|
|
40
|
+
#
|
|
41
|
+
# - Recurring r/ClaudeAI: sub-agent runs ``npm install -g <pkg>``
|
|
42
|
+
# to fix a broken setup, mutating global state without user
|
|
43
|
+
# approval.
|
|
44
|
+
# - "Refactor agent" pattern: parent dispatches Explore to fix a
|
|
45
|
+
# broken test; Explore commits its fix without showing diff to
|
|
46
|
+
# user. Force-push later overwrites work.
|
|
47
|
+
# - Database setup overreach: parent says "init the db", Explore
|
|
48
|
+
# runs ``psql DROP DATABASE && CREATE DATABASE``, wiping prod.
|
|
49
|
+
#
|
|
50
|
+
# Include via:
|
|
51
|
+
# include:
|
|
52
|
+
# - sponsio:capability/subagent
|
|
53
|
+
#
|
|
54
|
+
# Tool naming:
|
|
55
|
+
# The pack uses canonical ``exec`` for the shell tool. Claude
|
|
56
|
+
# Code's ``_host_subagent.yaml`` adds ``tool_rename: exec: Bash``
|
|
57
|
+
# so the rules apply to Claude Code's first-party Bash.
|
|
58
|
+
#
|
|
59
|
+
# Source tag convention:
|
|
60
|
+
# library:tier1.subagent
|
|
61
|
+
# =============================================================================
|
|
62
|
+
|
|
63
|
+
version: "1"
|
|
64
|
+
|
|
65
|
+
tools:
|
|
66
|
+
- name: exec
|
|
67
|
+
description: "Shell command execution (placeholder — replace with your tool's real name)"
|
|
68
|
+
params: "command: str"
|
|
69
|
+
|
|
70
|
+
agents:
|
|
71
|
+
"*":
|
|
72
|
+
contracts:
|
|
73
|
+
|
|
74
|
+
# =================================================================
|
|
75
|
+
# § 1 — Sub-agents may not commit, push, or rewrite git history
|
|
76
|
+
# =================================================================
|
|
77
|
+
#
|
|
78
|
+
# Forces the work product to flow back through the main
|
|
79
|
+
# conversation where the user sees every diff before it lands
|
|
80
|
+
# in version control. Sub-agent edits remain in the working
|
|
81
|
+
# tree; main agent (with user-visible reasoning) decides
|
|
82
|
+
# whether to commit.
|
|
83
|
+
|
|
84
|
+
- desc: "Sub-agents may not commit, push, force-push, or rewrite git history"
|
|
85
|
+
E:
|
|
86
|
+
pattern: arg_blacklist
|
|
87
|
+
args:
|
|
88
|
+
- exec
|
|
89
|
+
- command
|
|
90
|
+
# ``git\b[^|;&]*?\b<verb>\b`` matches the verb anywhere
|
|
91
|
+
# in the same shell command — defeats the
|
|
92
|
+
# ``git -c core.hooksPath=/dev/null commit`` bypass an
|
|
93
|
+
# earlier brittle ``git\s+commit`` regex missed. The
|
|
94
|
+
# ``[^|;&]*?`` keeps us inside the same command (won't
|
|
95
|
+
# cross ``|``, ``;``, ``&`` so ``git status; cat
|
|
96
|
+
# commit.log`` still passes).
|
|
97
|
+
- - "(?:^|[\\s;&|])git\\b[^|;&]*?\\b(commit|push|rebase|reflog|filter-branch|filter-repo)\\b"
|
|
98
|
+
- "(?:^|[\\s;&|])git\\b[^|;&]*?\\breset\\b[^|;&]*?\\b--hard\\b"
|
|
99
|
+
- "(?:^|[\\s;&|])git\\b[^|;&]*?\\b--force\\b"
|
|
100
|
+
- "(?:^|[\\s;&|])git\\b[^|;&]*?\\b-f\\b"
|
|
101
|
+
- "(?:^|[\\s;&|])git\\b[^|;&]*?\\brm\\b"
|
|
102
|
+
source: "library:tier1.subagent"
|
|
103
|
+
|
|
104
|
+
# =================================================================
|
|
105
|
+
# § 2 — Sub-agents may not install packages globally
|
|
106
|
+
# =================================================================
|
|
107
|
+
#
|
|
108
|
+
# Global package installs leak side-effects beyond the project
|
|
109
|
+
# working tree — they affect the user's whole machine. Main
|
|
110
|
+
# agent can request these explicitly with user visibility;
|
|
111
|
+
# sub-agents shouldn't decide unilaterally.
|
|
112
|
+
|
|
113
|
+
- desc: "Sub-agents may not install packages globally or system-wide"
|
|
114
|
+
E:
|
|
115
|
+
pattern: arg_blacklist
|
|
116
|
+
args:
|
|
117
|
+
- exec
|
|
118
|
+
- command
|
|
119
|
+
- - "(?:^|[\\s;&|])npm\\s+install\\s+(?:-g|--global)\\b"
|
|
120
|
+
- "(?:^|[\\s;&|])yarn\\s+global\\s+add\\b"
|
|
121
|
+
- "(?:^|[\\s;&|])pnpm\\s+add\\s+(?:-g|--global)\\b"
|
|
122
|
+
- "(?:^|[\\s;&|])pip\\s+install\\s+(?:-U\\s+)?[^-]"
|
|
123
|
+
- "(?:^|[\\s;&|])pipx\\s+install\\b"
|
|
124
|
+
- "(?:^|[\\s;&|])brew\\s+install\\b"
|
|
125
|
+
- "(?:^|[\\s;&|])apt(?:-get)?\\s+install\\b"
|
|
126
|
+
- "(?:^|[\\s;&|])sudo\\b"
|
|
127
|
+
source: "library:tier1.subagent"
|
|
128
|
+
|
|
129
|
+
# =================================================================
|
|
130
|
+
# § 3 — Sub-agents may not make outbound network calls
|
|
131
|
+
# =================================================================
|
|
132
|
+
#
|
|
133
|
+
# Curl / wget / ssh-out from a sub-agent context is a common
|
|
134
|
+
# exfiltration / supply-chain vector — the sub-agent doesn't
|
|
135
|
+
# have user-conversation context to know whether contacting an
|
|
136
|
+
# arbitrary host is appropriate.
|
|
137
|
+
|
|
138
|
+
- desc: "Sub-agents may not make outbound HTTP / shell-tunneled network calls"
|
|
139
|
+
E:
|
|
140
|
+
pattern: arg_blacklist
|
|
141
|
+
args:
|
|
142
|
+
- exec
|
|
143
|
+
- command
|
|
144
|
+
- - "(?:^|[\\s;&|])curl\\b"
|
|
145
|
+
- "(?:^|[\\s;&|])wget\\b"
|
|
146
|
+
- "(?:^|[\\s;&|])nc\\s+-"
|
|
147
|
+
- "(?:^|[\\s;&|])ssh\\s+\\S+@"
|
|
148
|
+
- "(?:^|[\\s;&|])scp\\b"
|
|
149
|
+
- "(?:^|[\\s;&|])rsync\\s+\\S+@"
|
|
150
|
+
source: "library:tier1.subagent"
|
|
151
|
+
|
|
152
|
+
# =================================================================
|
|
153
|
+
# § 4 — Sub-agents may not write outside scratch / project paths
|
|
154
|
+
# =================================================================
|
|
155
|
+
#
|
|
156
|
+
# System paths (/etc, /var, ~/, /usr/) are out of scope for any
|
|
157
|
+
# sub-agent task; if the work product needs them, the main
|
|
158
|
+
# agent should do it with user visibility.
|
|
159
|
+
|
|
160
|
+
- desc: "Sub-agents may not write to system / user-home paths"
|
|
161
|
+
E:
|
|
162
|
+
pattern: arg_blacklist
|
|
163
|
+
args:
|
|
164
|
+
- write
|
|
165
|
+
- file_path
|
|
166
|
+
- - "^/(etc|var|usr|opt|root|boot)/"
|
|
167
|
+
- "^/Users/[^/]+/[^/]" # macOS user home (any non-cwd)
|
|
168
|
+
- "^/home/[^/]+/[^/]" # Linux user home
|
|
169
|
+
- "^~/" # tilde-expanded home
|
|
170
|
+
- "^[A-Za-z]:\\\\\\\\Users\\\\\\\\" # Windows user home
|
|
171
|
+
source: "library:tier1.subagent"
|
|
172
|
+
|
|
173
|
+
- desc: "Sub-agents may not edit system / user-home paths"
|
|
174
|
+
E:
|
|
175
|
+
pattern: arg_blacklist
|
|
176
|
+
args:
|
|
177
|
+
- edit
|
|
178
|
+
- file_path
|
|
179
|
+
- - "^/(etc|var|usr|opt|root|boot)/"
|
|
180
|
+
- "^/Users/[^/]+/[^/]"
|
|
181
|
+
- "^/home/[^/]+/[^/]"
|
|
182
|
+
- "^~/"
|
|
183
|
+
- "^[A-Za-z]:\\\\\\\\Users\\\\\\\\"
|
|
184
|
+
source: "library:tier1.subagent"
|