@sponsio/sdk 0.1.0-alpha.3 → 0.2.0-alpha.10
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/bin/sponsio.cjs +11 -0
- package/contracts/benchmark/agentdojo.yaml +551 -0
- package/contracts/benchmark/odcv_bench.yaml +393 -0
- package/contracts/benchmark/redcode_exec.yaml +488 -0
- package/contracts/benchmark/swebench.yaml +176 -0
- package/contracts/benchmark/tau2_bench.yaml +984 -0
- package/contracts/capability/credentials.yaml +8 -8
- package/contracts/capability/database.yaml +6 -6
- package/contracts/capability/destructive.yaml +213 -0
- package/contracts/capability/factual-claims.yaml +79 -0
- package/contracts/capability/filesystem-strict.yaml +4 -4
- package/contracts/capability/filesystem.yaml +9 -9
- package/contracts/capability/host-config-integrity.yaml +318 -0
- package/contracts/capability/self-modify.yaml +260 -9
- package/contracts/capability/shell.yaml +43 -41
- package/contracts/capability/subagent.yaml +5 -5
- package/contracts/incident/claude-code-secret-bypass.yaml +57 -26
- package/contracts/incident/cursor-railway-wipe.yaml +63 -7
- package/contracts/incident/mcp-composition.yaml +12 -12
- package/contracts/incident/openclaw.yaml +47 -92
- package/contracts/incident/subagent-escape.yaml +4 -4
- package/contracts/premium/.gitkeep +5 -6
- package/dist/cli/check.d.ts +2 -0
- package/dist/cli/check.d.ts.map +1 -0
- package/dist/cli/check.js +283 -0
- package/dist/cli/check.js.map +1 -0
- package/dist/cli/cli.d.ts +25 -0
- package/dist/cli/cli.d.ts.map +1 -0
- package/dist/cli/cli.js +491 -0
- package/dist/cli/cli.js.map +1 -0
- package/dist/cli/config.d.ts +88 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +243 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/demo.d.ts +12 -0
- package/dist/cli/demo.d.ts.map +1 -0
- package/dist/cli/demo.js +350 -0
- package/dist/cli/demo.js.map +1 -0
- package/dist/cli/doctor.d.ts +11 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +201 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/eval.d.ts +2 -0
- package/dist/cli/eval.d.ts.map +1 -0
- package/dist/cli/eval.js +361 -0
- package/dist/cli/eval.js.map +1 -0
- package/dist/cli/explain.d.ts +2 -0
- package/dist/cli/explain.d.ts.map +1 -0
- package/dist/cli/explain.js +266 -0
- package/dist/cli/explain.js.map +1 -0
- package/dist/cli/export-sessions.d.ts +2 -0
- package/dist/cli/export-sessions.d.ts.map +1 -0
- package/dist/cli/export-sessions.js +182 -0
- package/dist/cli/export-sessions.js.map +1 -0
- package/dist/cli/export.d.ts +2 -0
- package/dist/cli/export.d.ts.map +1 -0
- package/dist/cli/export.js +222 -0
- package/dist/cli/export.js.map +1 -0
- package/dist/cli/extractors/common.d.ts +27 -0
- package/dist/cli/extractors/common.d.ts.map +1 -0
- package/dist/cli/extractors/common.js +107 -0
- package/dist/cli/extractors/common.js.map +1 -0
- package/dist/cli/extractors/generic.d.ts +37 -0
- package/dist/cli/extractors/generic.d.ts.map +1 -0
- package/dist/cli/extractors/generic.js +185 -0
- package/dist/cli/extractors/generic.js.map +1 -0
- package/dist/cli/extractors/langchain.d.ts +18 -0
- package/dist/cli/extractors/langchain.d.ts.map +1 -0
- package/dist/cli/extractors/langchain.js +86 -0
- package/dist/cli/extractors/langchain.js.map +1 -0
- package/dist/cli/extractors/vercel.d.ts +17 -0
- package/dist/cli/extractors/vercel.d.ts.map +1 -0
- package/dist/cli/extractors/vercel.js +46 -0
- package/dist/cli/extractors/vercel.js.map +1 -0
- package/dist/cli/index.d.ts +38 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +146 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +2 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +177 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/mode.d.ts +2 -0
- package/dist/cli/mode.d.ts.map +1 -0
- package/dist/cli/mode.js +71 -0
- package/dist/cli/mode.js.map +1 -0
- package/dist/cli/onboard.d.ts +88 -0
- package/dist/cli/onboard.d.ts.map +1 -0
- package/dist/cli/onboard.js +758 -0
- package/dist/cli/onboard.js.map +1 -0
- package/dist/cli/otlp.d.ts +27 -0
- package/dist/cli/otlp.d.ts.map +1 -0
- package/dist/cli/otlp.js +79 -0
- package/dist/cli/otlp.js.map +1 -0
- package/dist/cli/package.json +1 -0
- package/dist/cli/packs.d.ts +16 -0
- package/dist/cli/packs.d.ts.map +1 -0
- package/dist/cli/packs.js +190 -0
- package/dist/cli/packs.js.map +1 -0
- package/dist/cli/patterns.d.ts +16 -0
- package/dist/cli/patterns.d.ts.map +1 -0
- package/dist/cli/patterns.js +136 -0
- package/dist/cli/patterns.js.map +1 -0
- package/dist/cli/prompt.d.ts +2 -0
- package/dist/cli/prompt.d.ts.map +1 -0
- package/dist/cli/prompt.js +54 -0
- package/dist/cli/prompt.js.map +1 -0
- package/dist/cli/replay.d.ts +2 -0
- package/dist/cli/replay.d.ts.map +1 -0
- package/dist/cli/replay.js +196 -0
- package/dist/cli/replay.js.map +1 -0
- package/dist/cli/report.d.ts +57 -0
- package/dist/cli/report.d.ts.map +1 -0
- package/dist/cli/report.js +298 -0
- package/dist/cli/report.js.map +1 -0
- package/dist/cli/scan.d.ts +2 -0
- package/dist/cli/scan.d.ts.map +1 -0
- package/dist/cli/scan.js +172 -0
- package/dist/cli/scan.js.map +1 -0
- package/dist/cli/skill.d.ts +15 -0
- package/dist/cli/skill.d.ts.map +1 -0
- package/dist/cli/skill.js +131 -0
- package/dist/cli/skill.js.map +1 -0
- package/dist/cli/tokens.d.ts +31 -0
- package/dist/cli/tokens.d.ts.map +1 -0
- package/dist/cli/tokens.js +73 -0
- package/dist/cli/tokens.js.map +1 -0
- package/dist/cli/types.d.ts +46 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +3 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/cli/validate.d.ts +19 -0
- package/dist/cli/validate.d.ts.map +1 -0
- package/dist/cli/validate.js +205 -0
- package/dist/cli/validate.js.map +1 -0
- package/dist/cli/zod.d.ts +36 -0
- package/dist/cli/zod.d.ts.map +1 -0
- package/dist/cli/zod.js +226 -0
- package/dist/cli/zod.js.map +1 -0
- package/dist/cloud/client.d.ts +76 -0
- package/dist/cloud/client.d.ts.map +1 -0
- package/dist/cloud/client.js +204 -0
- package/dist/cloud/client.js.map +1 -0
- package/dist/cloud/evidence.d.ts +108 -0
- package/dist/cloud/evidence.d.ts.map +1 -0
- package/dist/cloud/evidence.js +184 -0
- package/dist/cloud/evidence.js.map +1 -0
- package/dist/cloud/privacy.d.ts +67 -0
- package/dist/cloud/privacy.d.ts.map +1 -0
- package/dist/cloud/privacy.js +170 -0
- package/dist/cloud/privacy.js.map +1 -0
- package/dist/cloud/view-model.d.ts +49 -0
- package/dist/cloud/view-model.d.ts.map +1 -0
- package/dist/cloud/view-model.js +172 -0
- package/dist/cloud/view-model.js.map +1 -0
- package/dist/contract.d.ts +13 -13
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +21 -21
- package/dist/contract.js.map +1 -1
- package/dist/core/config-loader.d.ts +1 -1
- package/dist/core/config-loader.d.ts.map +1 -1
- package/dist/core/config-loader.js +116 -26
- package/dist/core/config-loader.js.map +1 -1
- package/dist/core/evaluator.d.ts +11 -1
- package/dist/core/evaluator.d.ts.map +1 -1
- package/dist/core/evaluator.js +275 -13
- package/dist/core/evaluator.js.map +1 -1
- package/dist/core/formula.d.ts +76 -17
- package/dist/core/formula.d.ts.map +1 -1
- package/dist/core/formula.js +95 -1
- package/dist/core/formula.js.map +1 -1
- package/dist/core/grounding.d.ts +86 -3
- package/dist/core/grounding.d.ts.map +1 -1
- package/dist/core/grounding.js +485 -143
- package/dist/core/grounding.js.map +1 -1
- package/dist/core/nl-parser.d.ts +2 -2
- package/dist/core/nl-parser.d.ts.map +1 -1
- package/dist/core/nl-parser.js +690 -42
- package/dist/core/nl-parser.js.map +1 -1
- package/dist/core/pack-loader.d.ts.map +1 -1
- package/dist/core/pack-loader.js +14 -2
- package/dist/core/pack-loader.js.map +1 -1
- package/dist/core/parser.d.ts.map +1 -1
- package/dist/core/parser.js +30 -1
- package/dist/core/parser.js.map +1 -1
- package/dist/core/pattern-factory.d.ts +4 -4
- package/dist/core/pattern-factory.d.ts.map +1 -1
- package/dist/core/pattern-factory.js +59 -4
- package/dist/core/pattern-factory.js.map +1 -1
- package/dist/core/patterns.d.ts +196 -5
- package/dist/core/patterns.d.ts.map +1 -1
- package/dist/core/patterns.js +400 -13
- package/dist/core/patterns.js.map +1 -1
- package/dist/core/session-log.d.ts.map +1 -1
- package/dist/core/session-log.js +20 -1
- package/dist/core/session-log.js.map +1 -1
- package/dist/core/spans.d.ts +1 -1
- package/dist/core/spans.d.ts.map +1 -1
- package/dist/core/spans.js.map +1 -1
- package/dist/core/sto.d.ts +30 -66
- package/dist/core/sto.d.ts.map +1 -1
- package/dist/core/sto.js +39 -100
- package/dist/core/sto.js.map +1 -1
- package/dist/core/tool-names.d.ts +59 -0
- package/dist/core/tool-names.d.ts.map +1 -0
- package/dist/core/tool-names.js +128 -0
- package/dist/core/tool-names.js.map +1 -0
- package/dist/index.d.ts +227 -33
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +539 -152
- package/dist/index.js.map +1 -1
- package/dist/integrations/claude-agent.d.ts +5 -0
- package/dist/integrations/claude-agent.d.ts.map +1 -1
- package/dist/integrations/claude-agent.js +42 -13
- package/dist/integrations/claude-agent.js.map +1 -1
- package/dist/integrations/google-adk.d.ts +5 -3
- package/dist/integrations/google-adk.d.ts.map +1 -1
- package/dist/integrations/google-adk.js +9 -5
- package/dist/integrations/google-adk.js.map +1 -1
- package/dist/integrations/langchain.d.ts +7 -0
- package/dist/integrations/langchain.d.ts.map +1 -1
- package/dist/integrations/langchain.js +21 -3
- package/dist/integrations/langchain.js.map +1 -1
- package/dist/integrations/openai-agents.d.ts +22 -10
- package/dist/integrations/openai-agents.d.ts.map +1 -1
- package/dist/integrations/openai-agents.js +57 -16
- package/dist/integrations/openai-agents.js.map +1 -1
- package/dist/integrations/openai.d.ts +11 -0
- package/dist/integrations/openai.d.ts.map +1 -1
- package/dist/integrations/openai.js +130 -48
- package/dist/integrations/openai.js.map +1 -1
- package/dist/integrations/vercel-ai.d.ts +20 -9
- package/dist/integrations/vercel-ai.d.ts.map +1 -1
- package/dist/integrations/vercel-ai.js +131 -32
- package/dist/integrations/vercel-ai.js.map +1 -1
- package/dist/render/tokens.d.ts.map +1 -1
- package/dist/render/tokens.js +1 -0
- package/dist/render/tokens.js.map +1 -1
- package/package.json +41 -9
- package/prompts/onboard.md +259 -0
- package/prompts/scan.md +123 -0
- package/skills/SKILL.md +432 -0
- package/contracts/core/llm_safety.yaml +0 -130
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# sponsio/contracts/capability/host-config-integrity.yaml
|
|
3
|
+
#
|
|
4
|
+
# Capability pack — IDE host config integrity protection.
|
|
5
|
+
# Include in your per-host bucket (``_host_cursor`` /
|
|
6
|
+
# ``_host_claude_code`` / ``_host_codex`` / ``_host_windsurf``)
|
|
7
|
+
# so the agent under guard cannot disable Sponsio by rewriting
|
|
8
|
+
# the IDE's hook registration files.
|
|
9
|
+
#
|
|
10
|
+
# Threat model:
|
|
11
|
+
#
|
|
12
|
+
# Sponsio's enforcement on IDE coding agents is mediated through
|
|
13
|
+
# the host IDE's hook system: Cursor's ``.cursor/hooks.json``,
|
|
14
|
+
# Claude Code's ``~/.claude/settings.json`` /
|
|
15
|
+
# ``.claude/settings.local.json``, the per-project ``.claude/hooks/``
|
|
16
|
+
# directory, etc. These files declare WHICH commands the IDE invokes
|
|
17
|
+
# at which hook events. If the agent can rewrite them, it doesn't
|
|
18
|
+
# need to defeat any individual rule — it just removes the
|
|
19
|
+
# ``preToolUse`` entry that calls Sponsio at all, and on the next IDE
|
|
20
|
+
# restart Sponsio is silently absent. Every rule in
|
|
21
|
+
# ``capability/self-modify`` and ``incident/subagent-escape`` keeps
|
|
22
|
+
# working perfectly, against an enforcer that no longer exists.
|
|
23
|
+
# This is sponsio:capability/self-modify's blind spot — that pack
|
|
24
|
+
# protects the rule files; this pack protects the wiring that
|
|
25
|
+
# routes calls to the rule files in the first place.
|
|
26
|
+
#
|
|
27
|
+
# Concrete attack shapes observed (or trivially derivable from §1
|
|
28
|
+
# of self-modify.yaml's threat model):
|
|
29
|
+
#
|
|
30
|
+
# 1. ``Write .cursor/hooks.json '{"version":1,"hooks":{}}'``
|
|
31
|
+
# — wipe all hook entries; next Cursor restart, no enforcement.
|
|
32
|
+
# 2. ``Edit .cursor/hooks.json``: change ``"failClosed": true``
|
|
33
|
+
# to ``"failClosed": false`` — Sponsio is still wired in but
|
|
34
|
+
# any spawn / parse / runtime error fails open.
|
|
35
|
+
# 3. ``Write merge_hooks.py`` whose content opens hooks.json in
|
|
36
|
+
# 'w' mode (the §4 self-modify bypass shape, instantiated
|
|
37
|
+
# against hook config instead of plugin yaml).
|
|
38
|
+
# 4. ``python -c "open('.cursor/hooks.json','w').write('...')"``
|
|
39
|
+
# — the §5 self-modify interpreter-bypass shape against hook
|
|
40
|
+
# config.
|
|
41
|
+
# 5. ``echo '{}' > .cursor/hooks.json`` / ``sed -i 's/true/false/'``
|
|
42
|
+
# — bash redirect / mutator forms that the self-modify pack
|
|
43
|
+
# doesn't currently match because its regex anchors on
|
|
44
|
+
# ``.sponsio/plugins/...`` paths only.
|
|
45
|
+
# 6. ``truncate -s0 ~/.claude/settings.json`` / ``ln -sf /dev/null
|
|
46
|
+
# ~/.claude/settings.json`` / ``chmod 000 ~/.claude/settings.json``
|
|
47
|
+
# / ``rm -rf ~/.claude`` — no write at all, but the IDE reads an
|
|
48
|
+
# empty, foreign, or unreadable file and loads no hooks.
|
|
49
|
+
#
|
|
50
|
+
# What this pack denies:
|
|
51
|
+
#
|
|
52
|
+
# - Edit / Write / MultiEdit on the IDE hook registration files.
|
|
53
|
+
# - Bash redirect / sed / awk / tee that targets the same files.
|
|
54
|
+
# - Script *creation* whose content references a hook config path
|
|
55
|
+
# AND uses an open-for-write idiom (mirror of self-modify §4).
|
|
56
|
+
# - Interpreter invocation whose command line carries a hook
|
|
57
|
+
# config path (mirror of self-modify §5).
|
|
58
|
+
#
|
|
59
|
+
# What this pack does NOT deny:
|
|
60
|
+
#
|
|
61
|
+
# - ``sponsio host install`` and ``sponsio host uninstall``
|
|
62
|
+
# write hooks.json via Python subprocess from the Sponsio
|
|
63
|
+
# binary itself, not through the agent's Edit / Write tool
|
|
64
|
+
# surface — the legitimate install path is unaffected.
|
|
65
|
+
# - The user hand-editing hooks.json in their text editor
|
|
66
|
+
# similarly does not pass through the IDE's Edit / Write
|
|
67
|
+
# tool; this pack is silent on that surface.
|
|
68
|
+
# - Reads of hook config files are not denied. The file format
|
|
69
|
+
# is documented public knowledge; recon defense here would
|
|
70
|
+
# give negligible benefit at significant false-positive cost
|
|
71
|
+
# (legitimate "what hooks are registered?" debug queries).
|
|
72
|
+
# The subagent-escape pack covers the recon angle for
|
|
73
|
+
# sub-agents specifically.
|
|
74
|
+
#
|
|
75
|
+
# Pair with: ``sponsio:capability/self-modify`` (rule-file integrity)
|
|
76
|
+
# and ``sponsio:incident/subagent-escape`` (sub-agent containment).
|
|
77
|
+
# Together the three packs form the control-plane defense surface
|
|
78
|
+
# documented in ``agent_docs/control_plane_threat_model.md``.
|
|
79
|
+
#
|
|
80
|
+
# Include via:
|
|
81
|
+
# include:
|
|
82
|
+
# - sponsio:capability/host-config-integrity
|
|
83
|
+
#
|
|
84
|
+
# Source tag convention:
|
|
85
|
+
# library:tier1.host-config-integrity
|
|
86
|
+
# =============================================================================
|
|
87
|
+
|
|
88
|
+
version: "1"
|
|
89
|
+
|
|
90
|
+
agents:
|
|
91
|
+
"*":
|
|
92
|
+
contracts:
|
|
93
|
+
|
|
94
|
+
# =================================================================
|
|
95
|
+
# § 1 — No agent-mediated Edit of IDE hook config
|
|
96
|
+
# =================================================================
|
|
97
|
+
#
|
|
98
|
+
# Path set covers the wiring files for every host Sponsio
|
|
99
|
+
# currently supports. Add new hosts here as ``sponsio host
|
|
100
|
+
# install`` learns to register them.
|
|
101
|
+
#
|
|
102
|
+
# Anchor pattern note:
|
|
103
|
+
#
|
|
104
|
+
# The ``(^|/)`` anchor at the start of each alternative
|
|
105
|
+
# matches both absolute paths (``/Users/x/.cursor/hooks.json``)
|
|
106
|
+
# and project-relative paths (``.cursor/hooks.json``). The
|
|
107
|
+
# ``$`` anchors at the end pin the match to exact filenames,
|
|
108
|
+
# so a file named ``hooks.json.bak`` or ``settings.json.tmpl``
|
|
109
|
+
# stays editable.
|
|
110
|
+
#
|
|
111
|
+
# The Cursor MCP config (``.cursor/mcp.json``) is included
|
|
112
|
+
# because it's the file that declares Sponsio's MCP server
|
|
113
|
+
# to Cursor — same disable-by-rewrite attack shape, different
|
|
114
|
+
# file. Same for Claude Code's ``hooks/`` directory.
|
|
115
|
+
|
|
116
|
+
- desc: "Host agent must not edit IDE hook / MCP wiring files — disabling Sponsio's call site is privilege escalation"
|
|
117
|
+
G:
|
|
118
|
+
pattern: arg_blacklist
|
|
119
|
+
args:
|
|
120
|
+
- Edit
|
|
121
|
+
- file_path
|
|
122
|
+
- - "(^|/)\\.cursor/hooks\\.json$"
|
|
123
|
+
- "(^|/)\\.cursor/mcp\\.json$"
|
|
124
|
+
- "(^|/)\\.claude/settings\\.json$"
|
|
125
|
+
- "(^|/)\\.claude/settings\\.local\\.json$"
|
|
126
|
+
- "(^|/)\\.claude/hooks/[^/]+\\.json$"
|
|
127
|
+
- "(^|/)\\.codex/hooks\\.json$"
|
|
128
|
+
- "(^|/)\\.windsurf/hooks\\.json$"
|
|
129
|
+
source: "library:tier1.host-config-integrity"
|
|
130
|
+
|
|
131
|
+
# =================================================================
|
|
132
|
+
# § 2 — No agent-mediated Write of IDE hook config
|
|
133
|
+
# =================================================================
|
|
134
|
+
#
|
|
135
|
+
# ``Write`` overwrites the whole file — closes the
|
|
136
|
+
# "delete-and-recreate-empty" workaround a pure ``Edit`` block
|
|
137
|
+
# would leave open.
|
|
138
|
+
|
|
139
|
+
- desc: "Host agent must not overwrite IDE hook / MCP wiring files — use `sponsio host install` / hand-edit"
|
|
140
|
+
G:
|
|
141
|
+
pattern: arg_blacklist
|
|
142
|
+
args:
|
|
143
|
+
- Write
|
|
144
|
+
- file_path
|
|
145
|
+
- - "(^|/)\\.cursor/hooks\\.json$"
|
|
146
|
+
- "(^|/)\\.cursor/mcp\\.json$"
|
|
147
|
+
- "(^|/)\\.claude/settings\\.json$"
|
|
148
|
+
- "(^|/)\\.claude/settings\\.local\\.json$"
|
|
149
|
+
- "(^|/)\\.claude/hooks/[^/]+\\.json$"
|
|
150
|
+
- "(^|/)\\.codex/hooks\\.json$"
|
|
151
|
+
- "(^|/)\\.windsurf/hooks\\.json$"
|
|
152
|
+
source: "library:tier1.host-config-integrity"
|
|
153
|
+
|
|
154
|
+
# =================================================================
|
|
155
|
+
# § 3 — No agent-mediated MultiEdit of IDE hook config
|
|
156
|
+
# =================================================================
|
|
157
|
+
|
|
158
|
+
- desc: "Host agent must not multi-edit IDE hook / MCP wiring files — use `sponsio host install` / hand-edit"
|
|
159
|
+
G:
|
|
160
|
+
pattern: arg_blacklist
|
|
161
|
+
args:
|
|
162
|
+
- MultiEdit
|
|
163
|
+
- file_path
|
|
164
|
+
- - "(^|/)\\.cursor/hooks\\.json$"
|
|
165
|
+
- "(^|/)\\.cursor/mcp\\.json$"
|
|
166
|
+
- "(^|/)\\.claude/settings\\.json$"
|
|
167
|
+
- "(^|/)\\.claude/settings\\.local\\.json$"
|
|
168
|
+
- "(^|/)\\.claude/hooks/[^/]+\\.json$"
|
|
169
|
+
- "(^|/)\\.codex/hooks\\.json$"
|
|
170
|
+
- "(^|/)\\.windsurf/hooks\\.json$"
|
|
171
|
+
source: "library:tier1.host-config-integrity"
|
|
172
|
+
|
|
173
|
+
# =================================================================
|
|
174
|
+
# § 4 — No script creation that mutates IDE hook config
|
|
175
|
+
# (mirror of self-modify §4, against hook paths)
|
|
176
|
+
# =================================================================
|
|
177
|
+
#
|
|
178
|
+
# The same lookahead-conjunction shape as self-modify §4: deny
|
|
179
|
+
# only if the content carries BOTH a hook-config path AND a
|
|
180
|
+
# write-shape idiom. Pure docs that mention the path stay
|
|
181
|
+
# editable — the rule is narrow on purpose.
|
|
182
|
+
|
|
183
|
+
- desc: "Host agent must not write a script that mutates IDE hook / MCP wiring files"
|
|
184
|
+
G:
|
|
185
|
+
pattern: arg_blacklist
|
|
186
|
+
args:
|
|
187
|
+
- Write
|
|
188
|
+
- content
|
|
189
|
+
- - "(?s)(?=.*\\.cursor/(hooks|mcp)\\.json|\\.claude/(settings(\\.local)?\\.json|hooks/)|\\.codex/hooks\\.json|\\.windsurf/hooks\\.json).*(\\.write_text\\s*\\(|\\.write_bytes\\s*\\(|\\bf\\.write\\s*\\(|\\bopen\\s*\\([^)]*['\\\"][aw][\\+bxt]*['\\\"]\\s*\\)|\\bfs\\.(writeFileSync|appendFileSync|createWriteStream|writeFile)\\s*\\(|\\bFile\\.(open|write)\\s*\\(|\\b(IO|File)\\.write\\b)"
|
|
190
|
+
source: "library:tier1.host-config-integrity"
|
|
191
|
+
|
|
192
|
+
- desc: "Host agent must not edit a script that mutates IDE hook / MCP wiring files"
|
|
193
|
+
G:
|
|
194
|
+
pattern: arg_blacklist
|
|
195
|
+
args:
|
|
196
|
+
- Edit
|
|
197
|
+
- new_string
|
|
198
|
+
- - "(?s)(?=.*\\.cursor/(hooks|mcp)\\.json|\\.claude/(settings(\\.local)?\\.json|hooks/)|\\.codex/hooks\\.json|\\.windsurf/hooks\\.json).*(\\.write_text\\s*\\(|\\.write_bytes\\s*\\(|\\bf\\.write\\s*\\(|\\bopen\\s*\\([^)]*['\\\"][aw][\\+bxt]*['\\\"]\\s*\\)|\\bfs\\.(writeFileSync|appendFileSync|createWriteStream|writeFile)\\s*\\(|\\bFile\\.(open|write)\\s*\\(|\\b(IO|File)\\.write\\b)"
|
|
199
|
+
source: "library:tier1.host-config-integrity"
|
|
200
|
+
|
|
201
|
+
# =================================================================
|
|
202
|
+
# § 5 — No interpreter invocation targeting IDE hook config
|
|
203
|
+
# (mirror of self-modify §5, against hook paths)
|
|
204
|
+
# =================================================================
|
|
205
|
+
#
|
|
206
|
+
# Catches both inline forms (``python -c "...hooks.json..."``)
|
|
207
|
+
# and path-as-argument forms (``python merge.py
|
|
208
|
+
# .cursor/hooks.json``). The ``[^|\n]*`` between the
|
|
209
|
+
# interpreter token and the path token excludes cross-pipeline
|
|
210
|
+
# matches, same idiom as self-modify §5.
|
|
211
|
+
|
|
212
|
+
- desc: "Host agent must not run an interpreter against IDE hook / MCP wiring files"
|
|
213
|
+
G:
|
|
214
|
+
pattern: arg_blacklist
|
|
215
|
+
args:
|
|
216
|
+
- Bash
|
|
217
|
+
- command
|
|
218
|
+
- - "\\b(python|python2|python3|node|deno|bun|ruby|perl|php|lua|tclsh|R|Rscript)\\b[^|\\n]*(\\.cursor/(hooks|mcp)\\.json|\\.claude/(settings(\\.local)?\\.json|hooks/)|\\.codex/hooks\\.json|\\.windsurf/hooks\\.json)"
|
|
219
|
+
source: "library:tier1.host-config-integrity"
|
|
220
|
+
|
|
221
|
+
# =================================================================
|
|
222
|
+
# § 6 — No bash redirect / mutator targeting IDE hook config
|
|
223
|
+
# =================================================================
|
|
224
|
+
#
|
|
225
|
+
# Catches the cheap shell shapes that don't go through any
|
|
226
|
+
# interpreter: ``echo '{}' > hooks.json``, ``cat /dev/null >
|
|
227
|
+
# settings.json``, ``: > hooks.json``, ``sed -i 's/true/false/'
|
|
228
|
+
# hooks.json``, ``tee hooks.json``, ``cp empty.json
|
|
229
|
+
# hooks.json``, ``mv x hooks.json``, ``rm hooks.json``,
|
|
230
|
+
# ``truncate -s0``, ``ln -sf /dev/null``, ``chmod 000``.
|
|
231
|
+
#
|
|
232
|
+
# The pattern is "a mutator verb, then anything up to the next
|
|
233
|
+
# pipe / separator, then a hook-config path on the same command
|
|
234
|
+
# line". The gap between verb and path is what ``~/``, ``$HOME/``,
|
|
235
|
+
# ``"${HOME}/``, and an absolute prefix sit in: an earlier version
|
|
236
|
+
# required the path to follow the redirect operator immediately,
|
|
237
|
+
# so ``> .claude/settings.json`` was denied and ``> ~/.claude/
|
|
238
|
+
# settings.json`` was not (reported by kta1kri, 2026-10).
|
|
239
|
+
# ``truncate``, ``ln``, ``chmod``, ``chown`` and ``chattr`` are
|
|
240
|
+
# mutators too: an emptied, replaced, or unreadable settings file
|
|
241
|
+
# removes the hook exactly as an edited one does.
|
|
242
|
+
#
|
|
243
|
+
# Read-only patterns (``cat hooks.json``, ``jq . hooks.json``,
|
|
244
|
+
# ``grep ... hooks.json``, ``jq . settings.json > /tmp/backup``)
|
|
245
|
+
# are NOT matched — they carry no mutator verb in front of the
|
|
246
|
+
# path, and a redirect to a different file stops at that file.
|
|
247
|
+
# ``cp`` with the file as the *source* does match; a backup is
|
|
248
|
+
# the price of catching ``cp empty.json settings.json``.
|
|
249
|
+
#
|
|
250
|
+
# Edge case: ``python ... > hooks.json`` is already caught
|
|
251
|
+
# by §5 via the interpreter alternation. No double-deny harm
|
|
252
|
+
# since this is the deny side of the contract anyway.
|
|
253
|
+
|
|
254
|
+
- desc: "Host agent must not redirect / sed / tee / mv / rm / truncate / ln / chmod an IDE hook / MCP wiring file"
|
|
255
|
+
G:
|
|
256
|
+
pattern: arg_blacklist
|
|
257
|
+
args:
|
|
258
|
+
- Bash
|
|
259
|
+
- command
|
|
260
|
+
- - ">{1,2}\\|?\\s*[^|&;<>\\n]*(\\.cursor/(hooks|mcp)\\.json|\\.claude/(settings(\\.local)?\\.json|hooks/[^\\s|]+)|\\.codex/hooks\\.json|\\.windsurf/hooks\\.json)"
|
|
261
|
+
- "\\b(tee|cp|mv|rm|install|truncate|ln|chmod|chown|chattr|shred|unlink|rsync|dd|sponge|patch|ed|ex|vi|vim|nvim)\\b[^|&;\\n]*(\\.cursor/(hooks|mcp)\\.json|\\.claude/(settings(\\.local)?\\.json|hooks/[^\\s|]+)|\\.codex/hooks\\.json|\\.windsurf/hooks\\.json)"
|
|
262
|
+
- "\\bsed\\b[^|&;\\n]*\\s-[a-zA-Z]*i[^|&;\\n]*(\\.cursor/(hooks|mcp)\\.json|\\.claude/(settings(\\.local)?\\.json|hooks/[^\\s|]+)|\\.codex/hooks\\.json|\\.windsurf/hooks\\.json)"
|
|
263
|
+
- "\\bawk\\b[^|&;\\n]*\\s-i[^|&;\\n]*(\\.cursor/(hooks|mcp)\\.json|\\.claude/(settings(\\.local)?\\.json|hooks/[^\\s|]+)|\\.codex/hooks\\.json|\\.windsurf/hooks\\.json)"
|
|
264
|
+
source: "library:tier1.host-config-integrity"
|
|
265
|
+
|
|
266
|
+
# The rules above name the wiring FILE. Removing, moving, or making
|
|
267
|
+
# unreadable the directory that holds it (``rm -rf ~/.claude``,
|
|
268
|
+
# ``chmod 000 ~/.claude``, ``mv .cursor /tmp``) takes the hook
|
|
269
|
+
# registration with it. The lookahead stops at the directory name
|
|
270
|
+
# so ``~/.claude-backup`` and ``.claude/commands/x.md`` stay open.
|
|
271
|
+
- desc: "Host agent must not remove, move, or lock the IDE config directory that holds the hook wiring"
|
|
272
|
+
G:
|
|
273
|
+
pattern: arg_blacklist
|
|
274
|
+
args:
|
|
275
|
+
- Bash
|
|
276
|
+
- command
|
|
277
|
+
- - "\\b(rm|rmdir|mv|chmod|chown|chattr|ln)\\b[^|&;\\n]*(\\.claude|\\.cursor|\\.codex|\\.windsurf)/?(?![\\w./-])"
|
|
278
|
+
source: "library:tier1.host-config-integrity"
|
|
279
|
+
|
|
280
|
+
# -----------------------------------------------------------------------------
|
|
281
|
+
# Integration requirements
|
|
282
|
+
# -----------------------------------------------------------------------------
|
|
283
|
+
#
|
|
284
|
+
# - This pack assumes the host agent's Edit / Write / MultiEdit
|
|
285
|
+
# tool exposes ``file_path`` (Claude Code, Cursor, Codex all
|
|
286
|
+
# currently do). Hosts using a different param name will need
|
|
287
|
+
# the per-host adjustment that ``self-modify.yaml`` already
|
|
288
|
+
# documents.
|
|
289
|
+
#
|
|
290
|
+
# - The path set is the union of every Sponsio-supported host's
|
|
291
|
+
# wiring files. A given per-host bucket only needs the entries
|
|
292
|
+
# matching ITS host, but keeping the union here means the pack
|
|
293
|
+
# stays a single drop-in include with no per-host customization
|
|
294
|
+
# required at the call site. False-positive cost is zero —
|
|
295
|
+
# ``.cursor/hooks.json`` is only present in Cursor projects, so
|
|
296
|
+
# a Claude Code agent's edits to a non-existent path will never
|
|
297
|
+
# match anyway.
|
|
298
|
+
#
|
|
299
|
+
# - The ``"*"`` agent scope means these rules apply to every agent
|
|
300
|
+
# in any library that includes this pack. Recommended include
|
|
301
|
+
# site is the per-host bucket (``_host_cursor`` /
|
|
302
|
+
# ``_host_claude_code``), so the rules fire on the host that
|
|
303
|
+
# actually has the wiring file in question.
|
|
304
|
+
#
|
|
305
|
+
# - The bash mutator regex in §6 is intentionally broader than
|
|
306
|
+
# self-modify §5's interpreter-only regex. That's correct: hook
|
|
307
|
+
# config files are JSON, so non-interpreter shell mutators
|
|
308
|
+
# (sed, tee, cp) are the natural attack surface and they don't
|
|
309
|
+
# apply to .sponsio/plugins/*.yaml the same way (yaml requires
|
|
310
|
+
# careful editing that bash one-liners typically don't produce
|
|
311
|
+
# correctly, so the attack-shape distribution differs).
|
|
312
|
+
#
|
|
313
|
+
# - Read of hook config files is permitted. If a future incident
|
|
314
|
+
# pack wants to deny reads (e.g. for a defense-in-depth recon
|
|
315
|
+
# block similar to subagent-escape §4), add it as a separate
|
|
316
|
+
# ``incident/host-config-recon.yaml`` rather than expanding
|
|
317
|
+
# this capability pack — capability packs stay narrowly scoped
|
|
318
|
+
# to a single privilege boundary.
|
|
@@ -19,12 +19,42 @@
|
|
|
19
19
|
# time, but a sponsio-native rule is the right defense — it travels
|
|
20
20
|
# with the user's config to any host (Cursor, Codex, custom MCP).
|
|
21
21
|
#
|
|
22
|
+
# A second escalation vector lives one level up: an agent can
|
|
23
|
+
# ``import sponsio; print(sponsio.__file__)`` to locate the
|
|
24
|
+
# installed package, then edit the upstream capability bundle (or
|
|
25
|
+
# the evaluation engine itself) to delete the rule about to fire.
|
|
26
|
+
# This works on **any** user-writable Python install — editable
|
|
27
|
+
# (``pip install -e .``), ``pip install --user``, conda envs,
|
|
28
|
+
# venvs. Only system-wide ``/usr/lib/python*`` installs are safe
|
|
29
|
+
# by filesystem permission alone, and that's the minority of real
|
|
30
|
+
# deployments. This pack closes that vector.
|
|
31
|
+
#
|
|
22
32
|
# What this pack denies:
|
|
23
33
|
# - Edit / Write / MultiEdit on the legacy host guard config:
|
|
24
34
|
# ``~/.sponsio/plugins/_host/sponsio.yaml``
|
|
25
35
|
# - Edit / Write / MultiEdit on ANY per-plugin / per-MCP-server
|
|
26
36
|
# library (``~/.sponsio/plugins/<plugin-id>/sponsio.yaml``
|
|
27
37
|
# where ``<plugin-id>`` does NOT start with ``_host``).
|
|
38
|
+
# - Edit / Write / MultiEdit on the **upstream sponsio package** —
|
|
39
|
+
# specifically the contract bundles under
|
|
40
|
+
# ``sponsio/contracts/**/*.yaml`` and the evaluation-engine
|
|
41
|
+
# Python files (``guard_stdin``, ``config``, ``core``, plus
|
|
42
|
+
# anything under ``runtime/``, ``patterns/``, ``formulas/``,
|
|
43
|
+
# ``tracer/``, ``integrations/``, ``daemon/``, ``shim/``). Without
|
|
44
|
+
# this, an agent can ``import sponsio; print(sponsio.__file__)`` to locate
|
|
45
|
+
# the package, then edit a capability bundle (or monkey-patch
|
|
46
|
+
# the loader) to delete the rule about to fire — bypassing the
|
|
47
|
+
# per-bucket protection above.
|
|
48
|
+
# - Bash write operators (``>``, ``>>``, ``tee``, ``sed -i``,
|
|
49
|
+
# ``cp/mv/rm/dd``) targeting any of the above paths — the
|
|
50
|
+
# "shell shortcut" bypass of the structured Edit/Write block.
|
|
51
|
+
# - Bash interpreter invocations (``python``, ``node``, ``ruby``,
|
|
52
|
+
# ``perl``, ``deno``, ``bun``, etc.) targeting any protected
|
|
53
|
+
# path — the "run a script that does the write" bypass.
|
|
54
|
+
# - Write / Edit content that creates a script which itself
|
|
55
|
+
# mutates a protected path — the "stage a file then invoke it"
|
|
56
|
+
# two-step bypass.
|
|
57
|
+
#
|
|
28
58
|
# The legitimate way to add or update these bundles is the
|
|
29
59
|
# ``sponsio plugin install`` / ``sponsio plugin scan --apply``
|
|
30
60
|
# CLI — those write via Python subprocess, not the agent's
|
|
@@ -34,6 +64,17 @@
|
|
|
34
64
|
# own Edit/Write tools to weaken or rewrite a bundle that's
|
|
35
65
|
# about to fire against it.
|
|
36
66
|
#
|
|
67
|
+
# **Sponsio dev exception**: contributors working on the sponsio
|
|
68
|
+
# package itself routinely edit these files. Override in your
|
|
69
|
+
# project-level config:
|
|
70
|
+
#
|
|
71
|
+
# customized:
|
|
72
|
+
# - match: {source: "library:tier1.self-modify"}
|
|
73
|
+
# disabled: true
|
|
74
|
+
#
|
|
75
|
+
# This loses self-modify protection inside the sponsio repo —
|
|
76
|
+
# acceptable for the maintainer workflow, not for end users.
|
|
77
|
+
#
|
|
37
78
|
# Per-host buckets (``_host_cursor`` / ``_host_claude_code`` / etc.)
|
|
38
79
|
# carry their own targeted self-modify rules INSIDE their starter
|
|
39
80
|
# yaml, scoped to that one bucket only. The split keeps the
|
|
@@ -56,6 +97,13 @@
|
|
|
56
97
|
# ``sponsio onboard`` writes into the user's repo). That file
|
|
57
98
|
# IS supposed to evolve — onboard / scan / refresh all rewrite
|
|
58
99
|
# it. Locking it would break the core happy path.
|
|
100
|
+
# - Edits to non-engine sponsio package files (``cli.py``,
|
|
101
|
+
# ``discovery/``, ``reporting/``, ``scoring/``, ``demos/``,
|
|
102
|
+
# ``generation/``, ``models/``). These don't sit on the hook
|
|
103
|
+
# evaluation hot path — modifying them can't disable a rule
|
|
104
|
+
# against the agent's NEXT call. Sponsio contributors edit
|
|
105
|
+
# them frequently; locking them would make the dev workflow
|
|
106
|
+
# unworkable.
|
|
59
107
|
# - Direct edits via the user's IDE, terminal, or
|
|
60
108
|
# ``sponsio plugin install`` / ``plugin scan --apply`` CLI —
|
|
61
109
|
# those don't go through the ``Edit`` / ``Write`` tool surface
|
|
@@ -82,11 +130,11 @@ agents:
|
|
|
82
130
|
contracts:
|
|
83
131
|
|
|
84
132
|
# =================================================================
|
|
85
|
-
# § 1 — No agent-mediated Edit of
|
|
133
|
+
# § 1 — No agent-mediated Edit of any sponsio config or engine
|
|
86
134
|
# =================================================================
|
|
87
135
|
|
|
88
|
-
- desc: "Host agent must not edit any sponsio guard config
|
|
89
|
-
|
|
136
|
+
- desc: "Host agent must not edit any sponsio guard config or engine file — use `sponsio plugin install` / `plugin scan --apply`, or hand-edit"
|
|
137
|
+
G:
|
|
90
138
|
pattern: arg_blacklist
|
|
91
139
|
args:
|
|
92
140
|
- Edit
|
|
@@ -101,10 +149,17 @@ agents:
|
|
|
101
149
|
# still manage downstream plugin policy.
|
|
102
150
|
- - "(^|/)\\.sponsio/plugins/_host/sponsio\\.yaml$"
|
|
103
151
|
- "(^|/)\\.sponsio/plugins/[^_/][^/]*/sponsio\\.yaml$"
|
|
152
|
+
# Upstream sponsio package — contract bundles (rule data)
|
|
153
|
+
# and evaluation-engine .py files. Anchored on
|
|
154
|
+
# ``(^|/)sponsio/...`` so it matches editable installs,
|
|
155
|
+
# site-packages, --user installs, conda/venv all the same.
|
|
156
|
+
- "(^|/)sponsio/contracts/[^/]+/[^/]+\\.yaml$"
|
|
157
|
+
- "(^|/)sponsio/(guard_stdin|config|core)\\.py$"
|
|
158
|
+
- "(^|/)sponsio/(runtime|patterns|formulas|tracer|integrations|daemon|shim)/[^/]+\\.py$"
|
|
104
159
|
source: "library:tier1.self-modify"
|
|
105
160
|
|
|
106
161
|
# =================================================================
|
|
107
|
-
# § 2 — No agent-mediated Write of
|
|
162
|
+
# § 2 — No agent-mediated Write of any sponsio config or engine
|
|
108
163
|
# =================================================================
|
|
109
164
|
#
|
|
110
165
|
# ``Write`` is the "create or overwrite whole file" tool —
|
|
@@ -112,28 +167,219 @@ agents:
|
|
|
112
167
|
# rules removed" workaround a pure ``Edit`` block would leave
|
|
113
168
|
# open.
|
|
114
169
|
|
|
115
|
-
- desc: "Host agent must not overwrite any sponsio guard config
|
|
116
|
-
|
|
170
|
+
- desc: "Host agent must not overwrite any sponsio guard config or engine file — use the CLI or hand-edit"
|
|
171
|
+
G:
|
|
117
172
|
pattern: arg_blacklist
|
|
118
173
|
args:
|
|
119
174
|
- Write
|
|
120
175
|
- file_path
|
|
121
176
|
- - "(^|/)\\.sponsio/plugins/_host/sponsio\\.yaml$"
|
|
122
177
|
- "(^|/)\\.sponsio/plugins/[^_/][^/]*/sponsio\\.yaml$"
|
|
178
|
+
- "(^|/)sponsio/contracts/[^/]+/[^/]+\\.yaml$"
|
|
179
|
+
- "(^|/)sponsio/(guard_stdin|config|core)\\.py$"
|
|
180
|
+
- "(^|/)sponsio/(runtime|patterns|formulas|tracer|integrations|daemon|shim)/[^/]+\\.py$"
|
|
123
181
|
source: "library:tier1.self-modify"
|
|
124
182
|
|
|
125
183
|
# =================================================================
|
|
126
|
-
# § 3 — No agent-mediated MultiEdit of
|
|
184
|
+
# § 3 — No agent-mediated MultiEdit of any sponsio config or engine
|
|
127
185
|
# =================================================================
|
|
128
186
|
|
|
129
|
-
- desc: "Host agent must not multi-edit any sponsio guard config
|
|
130
|
-
|
|
187
|
+
- desc: "Host agent must not multi-edit any sponsio guard config or engine file — use the CLI or hand-edit"
|
|
188
|
+
G:
|
|
131
189
|
pattern: arg_blacklist
|
|
132
190
|
args:
|
|
133
191
|
- MultiEdit
|
|
134
192
|
- file_path
|
|
135
193
|
- - "(^|/)\\.sponsio/plugins/_host/sponsio\\.yaml$"
|
|
136
194
|
- "(^|/)\\.sponsio/plugins/[^_/][^/]*/sponsio\\.yaml$"
|
|
195
|
+
- "(^|/)sponsio/contracts/[^/]+/[^/]+\\.yaml$"
|
|
196
|
+
- "(^|/)sponsio/(guard_stdin|config|core)\\.py$"
|
|
197
|
+
- "(^|/)sponsio/(runtime|patterns|formulas|tracer|integrations|daemon|shim)/[^/]+\\.py$"
|
|
198
|
+
source: "library:tier1.self-modify"
|
|
199
|
+
|
|
200
|
+
# =================================================================
|
|
201
|
+
# § 4 — No agent-mediated creation of a *script* that writes to
|
|
202
|
+
# a protected path (the "Python file I/O bypass")
|
|
203
|
+
# =================================================================
|
|
204
|
+
#
|
|
205
|
+
# Observed bypass: agent's ``Write`` to the host bucket gets denied
|
|
206
|
+
# by §1–§3, agent recovers by writing a small ``merge_host_*.py``
|
|
207
|
+
# script in the workspace whose content opens the host bucket
|
|
208
|
+
# path in write mode and mutates it. The script's file_path is a
|
|
209
|
+
# normal repo path so §1–§3 don't fire; the Bash invocation
|
|
210
|
+
# ``python merge.py`` doesn't carry the host bucket path on the
|
|
211
|
+
# command line so §6 below also doesn't fire. The signal IS
|
|
212
|
+
# present: the script's *content* mentions a protected path
|
|
213
|
+
# AND uses an open-for-write idiom.
|
|
214
|
+
#
|
|
215
|
+
# The lookahead form ``(?s)(?=.*PATH).*(WRITE_SHAPE)`` requires
|
|
216
|
+
# both signals in the same content blob — pure docs that mention
|
|
217
|
+
# the path stay editable; only files that mention the path AND
|
|
218
|
+
# carry write-shaped code are denied.
|
|
219
|
+
#
|
|
220
|
+
# The PATH alternation covers all four protected families:
|
|
221
|
+
# - host buckets (~/.sponsio/plugins/_host*/...)
|
|
222
|
+
# - per-plugin libs (~/.sponsio/plugins/<id>/...)
|
|
223
|
+
# - upstream contract bundles (sponsio/contracts/.../*.yaml)
|
|
224
|
+
# - upstream engine .py (sponsio/{guard_stdin,config,core}.py
|
|
225
|
+
# and sponsio/{runtime,patterns,formulas,tracer,
|
|
226
|
+
# integrations}/*.py)
|
|
227
|
+
#
|
|
228
|
+
# Tools covered: Write.content, Edit.new_string,
|
|
229
|
+
# MultiEdit.edits[*].new_string (the ``new_string`` projection
|
|
230
|
+
# that grounding extracts from MultiEdit's edit list).
|
|
231
|
+
|
|
232
|
+
- desc: "Host agent must not write a script that mutates a sponsio guard config or engine file — use `sponsio plugin append`/`install`"
|
|
233
|
+
G:
|
|
234
|
+
pattern: arg_blacklist
|
|
235
|
+
args:
|
|
236
|
+
- Write
|
|
237
|
+
- content
|
|
238
|
+
- - "(?s)(?=.*(\\.sponsio/plugins/(_host[^/]*|[^_/][^/]*)/sponsio\\.yaml|sponsio/contracts/[^/]+/[^/]+\\.yaml|sponsio/(guard_stdin|config|core)\\.py|sponsio/(runtime|patterns|formulas|tracer|integrations|daemon|shim)/[^/]+\\.py)).*(\\.write_text\\s*\\(|\\.write_bytes\\s*\\(|\\bf\\.write\\s*\\(|\\bopen\\s*\\([^)]*['\\\"][aw][\\+bxt]*['\\\"]\\s*\\)|\\bfs\\.(writeFileSync|appendFileSync|createWriteStream|writeFile)\\s*\\(|\\bFile\\.(open|write)\\s*\\(|\\b(IO|File)\\.write\\b)"
|
|
239
|
+
source: "library:tier1.self-modify"
|
|
240
|
+
|
|
241
|
+
- desc: "Host agent must not edit a script that mutates a sponsio guard config or engine file — use `sponsio plugin append`/`install`"
|
|
242
|
+
G:
|
|
243
|
+
pattern: arg_blacklist
|
|
244
|
+
args:
|
|
245
|
+
- Edit
|
|
246
|
+
- new_string
|
|
247
|
+
- - "(?s)(?=.*(\\.sponsio/plugins/(_host[^/]*|[^_/][^/]*)/sponsio\\.yaml|sponsio/contracts/[^/]+/[^/]+\\.yaml|sponsio/(guard_stdin|config|core)\\.py|sponsio/(runtime|patterns|formulas|tracer|integrations|daemon|shim)/[^/]+\\.py)).*(\\.write_text\\s*\\(|\\.write_bytes\\s*\\(|\\bf\\.write\\s*\\(|\\bopen\\s*\\([^)]*['\\\"][aw][\\+bxt]*['\\\"]\\s*\\)|\\bfs\\.(writeFileSync|appendFileSync|createWriteStream|writeFile)\\s*\\(|\\bFile\\.(open|write)\\s*\\(|\\b(IO|File)\\.write\\b)"
|
|
248
|
+
source: "library:tier1.self-modify"
|
|
249
|
+
|
|
250
|
+
# =================================================================
|
|
251
|
+
# § 5 — No interpreter invocation that targets a protected path
|
|
252
|
+
# =================================================================
|
|
253
|
+
#
|
|
254
|
+
# Catches the inline form (``python -c "...sponsio/contracts/..."``)
|
|
255
|
+
# and the path-as-argument form (``python merge.py
|
|
256
|
+
# ~/.sponsio/plugins/...sponsio.yaml``). Pairs with §4 — that
|
|
257
|
+
# rule blocks the *creation* of bypass scripts, this rule blocks
|
|
258
|
+
# the *invocation* path that doesn't go through a separate file.
|
|
259
|
+
#
|
|
260
|
+
# The interpreter list is the common scripting set; add new ones
|
|
261
|
+
# via a project-local ``customized:`` extending the regex if your
|
|
262
|
+
# environment uses a less common runtime. The trailing path
|
|
263
|
+
# regex is the same shape as §1–§3 / §6 so the privilege boundary
|
|
264
|
+
# stays consistent across tools.
|
|
265
|
+
#
|
|
266
|
+
# Legitimate sponsio CLIs that read the path
|
|
267
|
+
# (``sponsio validate --config <path>``, ``sponsio plugin
|
|
268
|
+
# show <name>``) are not blocked because the leading verb is
|
|
269
|
+
# ``sponsio``, not an interpreter alias. ``sponsio plugin
|
|
270
|
+
# append --target <plugin-id>`` doesn't pass the path at all —
|
|
271
|
+
# it takes the plugin id and resolves the path internally.
|
|
272
|
+
|
|
273
|
+
- desc: "Host agent must not run an interpreter against a sponsio guard config or engine file — use `sponsio plugin append`/`install`"
|
|
274
|
+
G:
|
|
275
|
+
pattern: arg_blacklist
|
|
276
|
+
args:
|
|
277
|
+
- Bash
|
|
278
|
+
- command
|
|
279
|
+
# ``[^|\n]*`` excludes pipe + newline so we don't match
|
|
280
|
+
# across pipelines, but keeps ``;`` permitted because
|
|
281
|
+
# Python's ``-c "..."`` payloads commonly use it as a
|
|
282
|
+
# statement separator (``python -c "import x; x.do()"``).
|
|
283
|
+
- - "\\b(python|python2|python3|node|deno|bun|ruby|perl|php|lua|tclsh|R|Rscript)\\b[^|\\n]*(\\.sponsio/plugins/(_host[^/]*|[^_/][^/]*)/sponsio\\.yaml|sponsio/contracts/[^/]+/[^/]+\\.yaml|sponsio/(guard_stdin|config|core)\\.py|sponsio/(runtime|patterns|formulas|tracer|integrations|daemon|shim)/[^/]+\\.py)"
|
|
284
|
+
source: "library:tier1.self-modify"
|
|
285
|
+
|
|
286
|
+
# =================================================================
|
|
287
|
+
# § 6 — No Bash write operators against a protected path
|
|
288
|
+
# =================================================================
|
|
289
|
+
#
|
|
290
|
+
# The "shell shortcut" bypass: ``echo … > sponsio/contracts/…``,
|
|
291
|
+
# ``sed -i … sponsio/runtime/monitor.py``, ``cp /tmp/replacement
|
|
292
|
+
# sponsio/patterns/library.py``. These don't go through Edit /
|
|
293
|
+
# Write / MultiEdit so §1–§3 don't see them. Each operator
|
|
294
|
+
# gets one regex, applied against the same alternation of
|
|
295
|
+
# protected paths as §1–§3.
|
|
296
|
+
#
|
|
297
|
+
# Operator list: redirect (``>``, ``>>``), explicit writers
|
|
298
|
+
# (``tee``, ``sed -i``), file movers (``cp``, ``mv``, ``rm``,
|
|
299
|
+
# ``dd``). The ``[^|&;]*`` segment between operator and path
|
|
300
|
+
# keeps each match local to its own command (won't cross
|
|
301
|
+
# pipelines or statement separators).
|
|
302
|
+
|
|
303
|
+
- desc: "Host agent must not redirect Bash output (`>` / `>>`) into a sponsio guard config or engine file"
|
|
304
|
+
G:
|
|
305
|
+
pattern: arg_blacklist
|
|
306
|
+
args:
|
|
307
|
+
- Bash
|
|
308
|
+
- command
|
|
309
|
+
- - ">{1,2}\\|?\\s*[^|&;<>\\n]*(\\.sponsio/plugins/(_host[^/]*|[^_/][^/]*)/sponsio\\.yaml|sponsio/contracts/[^/]+/[^/]+\\.yaml|sponsio/(guard_stdin|config|core)\\.py|sponsio/(runtime|patterns|formulas|tracer|integrations|daemon|shim)/[^/]+\\.py)"
|
|
310
|
+
source: "library:tier1.self-modify"
|
|
311
|
+
|
|
312
|
+
- desc: "Host agent must not `tee` into a sponsio guard config or engine file"
|
|
313
|
+
G:
|
|
314
|
+
pattern: arg_blacklist
|
|
315
|
+
args:
|
|
316
|
+
- Bash
|
|
317
|
+
- command
|
|
318
|
+
- - "\\btee\\b\\s+[^|&;]*(\\.sponsio/plugins/(_host[^/]*|[^_/][^/]*)/sponsio\\.yaml|sponsio/contracts/[^/]+/[^/]+\\.yaml|sponsio/(guard_stdin|config|core)\\.py|sponsio/(runtime|patterns|formulas|tracer|integrations|daemon|shim)/[^/]+\\.py)"
|
|
319
|
+
source: "library:tier1.self-modify"
|
|
320
|
+
|
|
321
|
+
- desc: "Host agent must not `sed -i` a sponsio guard config or engine file"
|
|
322
|
+
G:
|
|
323
|
+
pattern: arg_blacklist
|
|
324
|
+
args:
|
|
325
|
+
- Bash
|
|
326
|
+
- command
|
|
327
|
+
- - "\\bsed\\b\\s+-i\\b[^|&;]*(\\.sponsio/plugins/(_host[^/]*|[^_/][^/]*)/sponsio\\.yaml|sponsio/contracts/[^/]+/[^/]+\\.yaml|sponsio/(guard_stdin|config|core)\\.py|sponsio/(runtime|patterns|formulas|tracer|integrations|daemon|shim)/[^/]+\\.py)"
|
|
328
|
+
source: "library:tier1.self-modify"
|
|
329
|
+
|
|
330
|
+
- desc: "Host agent must not `cp` / `mv` / `rm` / `dd` against a sponsio guard config or engine file"
|
|
331
|
+
G:
|
|
332
|
+
pattern: arg_blacklist
|
|
333
|
+
args:
|
|
334
|
+
- Bash
|
|
335
|
+
- command
|
|
336
|
+
- - "\\b(cp|mv|rm|dd)\\b\\s+[^|&;]*(\\.sponsio/plugins/(_host[^/]*|[^_/][^/]*)/sponsio\\.yaml|sponsio/contracts/[^/]+/[^/]+\\.yaml|sponsio/(guard_stdin|config|core)\\.py|sponsio/(runtime|patterns|formulas|tracer|integrations|daemon|shim)/[^/]+\\.py)"
|
|
337
|
+
source: "library:tier1.self-modify"
|
|
338
|
+
|
|
339
|
+
# A file can be emptied, replaced, or made unreadable without a
|
|
340
|
+
# write operator: ``truncate -s0``, ``ln -sf /dev/null``,
|
|
341
|
+
# ``chmod 000`` (reported by kta1kri, 2026-10, against the hook
|
|
342
|
+
# wiring; the rule file has the same exposure). The hook fails
|
|
343
|
+
# closed on an empty or unreadable library, so for the rule file
|
|
344
|
+
# these are denial of service rather than bypass, but they are
|
|
345
|
+
# the agent reaching for its own guard all the same.
|
|
346
|
+
- desc: "Host agent must not truncate / link over / lock a sponsio guard config or engine file"
|
|
347
|
+
G:
|
|
348
|
+
pattern: arg_blacklist
|
|
349
|
+
args:
|
|
350
|
+
- Bash
|
|
351
|
+
- command
|
|
352
|
+
- - "\\b(truncate|ln|chmod|chown|chattr|shred|unlink|install|rsync|sponge|patch|ed|ex|vi|vim|nvim)\\b\\s+[^|&;]*(\\.sponsio/plugins/(_host[^/]*|[^_/][^/]*)/sponsio\\.yaml|sponsio/contracts/[^/]+/[^/]+\\.yaml|sponsio/(guard_stdin|config|core)\\.py|sponsio/(runtime|patterns|formulas|tracer|integrations|daemon|shim)/[^/]+\\.py)"
|
|
353
|
+
source: "library:tier1.self-modify"
|
|
354
|
+
|
|
355
|
+
# The rules above name the rule FILE. Removing or moving a directory
|
|
356
|
+
# that contains it (``rm -rf ~/.sponsio``, ``mv ~/.sponsio/plugins
|
|
357
|
+
# /tmp``) took every library with it, after which each tool was
|
|
358
|
+
# "unconfigured" and ran, and ``defaults.unconfigured: deny`` went
|
|
359
|
+
# too, since it lives in the host library. The lookahead stops at
|
|
360
|
+
# the directory name so ``~/.sponsio-backup`` stays allowed.
|
|
361
|
+
- desc: "Host agent must not remove, move, or lock the sponsio config directory or a library bucket"
|
|
362
|
+
G:
|
|
363
|
+
pattern: arg_blacklist
|
|
364
|
+
args:
|
|
365
|
+
- Bash
|
|
366
|
+
- command
|
|
367
|
+
- - "\\b(rm|mv|rmdir|chmod|chown|chattr|ln)\\b[^|&;\\n]*\\.sponsio(/plugins(/[\\w-]+)?)?/?(?![\\w./-])"
|
|
368
|
+
source: "library:tier1.self-modify"
|
|
369
|
+
|
|
370
|
+
# The daemon holds a rule change that removes a contract until
|
|
371
|
+
# someone runs `sponsio rules reload`. At Tier 1 that someone could
|
|
372
|
+
# be the agent, approving its own weakening; the same goes for
|
|
373
|
+
# starting a daemon that applies every change (`--watch`), switching
|
|
374
|
+
# a host to observe, or removing the hooks. These belong to the
|
|
375
|
+
# person at their own terminal.
|
|
376
|
+
- desc: "Host agent must not approve its own rule changes, or switch Sponsio to observe or off"
|
|
377
|
+
G:
|
|
378
|
+
pattern: arg_blacklist
|
|
379
|
+
args:
|
|
380
|
+
- Bash
|
|
381
|
+
- command
|
|
382
|
+
- - "\\bsponsio\\b[^|&;\\n]*\\b(rules\\s+reload|admin\\b|daemon\\s+run\\b[^|&;\\n]*--watch|host\\s+uninstall|host\\s+install\\b[^|&;\\n]*--mode[\\s=]+observe)"
|
|
137
383
|
source: "library:tier1.self-modify"
|
|
138
384
|
|
|
139
385
|
# -----------------------------------------------------------------------------
|
|
@@ -150,3 +396,8 @@ agents:
|
|
|
150
396
|
# trying to touch this same file is covered by the broader
|
|
151
397
|
# ``sponsio:incident/subagent-escape`` pack, which denies the
|
|
152
398
|
# ENTIRE ``~/.sponsio/`` tree to sub-agents.
|
|
399
|
+
#
|
|
400
|
+
# - Upstream package paths are matched WITHOUT the install prefix —
|
|
401
|
+
# ``(^|/)sponsio/contracts/...`` matches editable installs, site-
|
|
402
|
+
# packages, --user installs, conda/venv all the same. The first
|
|
403
|
+
# ``sponsio/`` directory boundary anchors the match cleanly.
|