spacehub

surshar / claude-code

Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.

Built by Anthropic · github.com/anthropics

claude-code / mods / sec-default / tests / register.test.ts
496 lines · 13.6 KBRaw
1import { describe, expect, test, tier } from 'claude-code/testing'
2
3import Hooks from '../hooks'
4import Fixtures from './fixtures'
5
6tier('prepend')
7
8describe('register', () => {
9 test(
10 'under an MCP allowlist a plugin the person installed may not add a tool',
11 {
12 plugins: [
13 Fixtures.registering('suite', 'prepend'),
14 Fixtures.registering('mine'),
15 Fixtures.registering('bundled', 'builtin'),
16 ],
17 },
18 async ($, on) => {
19 on('settings.read', () => ({ value: Fixtures.ALLOWLIST }))
20
21 const registered = Fixtures.toolsRegistered(on)
22
23 await $.session.start(Fixtures.SESSION)
24
25 expect(registered).toEqual(['suite', 'bundled'])
26 },
27 )
28
29 test(
30 'with no allowlist, a plugin the person installed adds its tool',
31 { plugins: [Fixtures.registering('mine')] },
32 async ($, on) => {
33 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
34
35 const registered = Fixtures.toolsRegistered(on)
36
37 await $.session.start(Fixtures.SESSION)
38
39 expect(registered).toEqual(['mine'])
40 },
41 )
42
43 test(
44 "an organization's registration passes over a refusing user plugin",
45 {
46 plugins: [
47 Fixtures.registering('suite', 'prepend'),
48 Fixtures.denying,
49 Fixtures.registering('bundled', 'builtin'),
50 ],
51 },
52 async ($, on) => {
53 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
54
55 const registered = Fixtures.toolsRegistered(on)
56
57 await $.session.start(Fixtures.SESSION)
58
59 expect(
60 registered,
61 "a built-in's registration still meets the user's deny",
62 ).toEqual(['suite'])
63 },
64 )
65
66 test(
67 'a policy that cannot be read counts as one in force',
68 { plugins: [Fixtures.registering('mine')] },
69 async ($, on) => {
70 const stopPolicy = Fixtures.policyUntilStopped(
71 on,
72 Fixtures.NO_ALLOWLIST,
73 'managed settings unreadable',
74 )
75
76 const registered = Fixtures.toolsRegistered(on)
77
78 on('prompt.section', ($, e) => ({ text: e.text }))
79
80 await $.prompt.section(Fixtures.MEMORY)
81 stopPolicy()
82 await $.session.start(Fixtures.SESSION)
83
84 expect(registered).toEqual([])
85 },
86 )
87
88 test(
89 'the organization tools are listed as its tiers listed them',
90 { plugins: [Fixtures.relabeling, Fixtures.listing] },
91 async ($, on) => {
92 on('settings.read', () => ({ value: Fixtures.ALLOWLIST }))
93 on('tool.list', () => ({ value: [...Fixtures.TOOLS] }))
94
95 const { text } = await $.command.run(Fixtures.TOOLS_COMMAND)
96
97 expect(text?.split('\n')).toEqual([
98 'mcp__corp__search: Searches the corp wiki.',
99 'Bash: relabeled',
100 ])
101 },
102 )
103
104 test(
105 'a server policy delivers itself counts as a tool policy, unlisted',
106 { plugins: [Fixtures.relabeling, Fixtures.listing] },
107 async ($, on) => {
108 on('settings.read', () => ({ value: Fixtures.SERVER_POLICY }))
109 on('tool.list', () => ({ value: [...Fixtures.TOOLS] }))
110
111 const { text } = await $.command.run(Fixtures.TOOLS_COMMAND)
112
113 expect(text?.split('\n')).toEqual([
114 'mcp__corp__search: Searches the corp wiki.',
115 'Bash: relabeled',
116 ])
117 },
118 )
119
120 test(
121 "with no policy to read the organization's listing stands whole",
122 { plugins: [Fixtures.relabeling, Fixtures.listing] },
123 async ($, on) => {
124 const stopPolicy = Fixtures.policyUntilStopped(
125 on,
126 Fixtures.ALLOWLIST,
127 'settings unreadable',
128 )
129
130 on('prompt.section', ($, e) => ({ text: e.text }))
131 on('tool.list', () => ({ value: [...Fixtures.TOOLS] }))
132
133 await $.prompt.section(Fixtures.MEMORY)
134 stopPolicy()
135
136 const { text } = await $.command.run(Fixtures.TOOLS_COMMAND)
137
138 expect(text?.split('\n')).toEqual([
139 'mcp__corp__search: Searches the corp wiki.',
140 'Bash: Runs a command.',
141 ])
142 },
143 )
144
145 test(
146 'a prompt section passes over the plugins the person installed',
147 { plugins: [Fixtures.dropping, Fixtures.signing] },
148 async ($, on) => {
149 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
150 on('prompt.section', ($, e) => ({ text: e.text }))
151
152 expect(await $.prompt.section(Fixtures.MEMORY)).toEqual({
153 text: 'the org says hi (signed)',
154 })
155 },
156 )
157
158 test(
159 "the system prompt's sections pass over the plugins the person installed",
160 { plugins: [Fixtures.emptying, Fixtures.heading] },
161 async ($, on) => {
162 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
163
164 on('prompt.compose', () => ({
165 sections: [{ id: 'body', text: 'the body', scope: 'shared' }],
166 }))
167
168 expect(await $.prompt.compose(Fixtures.COMPOSED)).toEqual({
169 sections: [
170 { id: 'heading:org', text: 'the org says hi', scope: 'shared' },
171 { id: 'body', text: 'the body', scope: 'shared' },
172 ],
173 })
174 },
175 )
176
177 test(
178 "nor does a person's plugin drop or reword a section it asked for",
179 { plugins: [Fixtures.rewording, Fixtures.heading] },
180 async ($, on) => {
181 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
182
183 on('prompt.compose', () => ({
184 sections: [{ id: 'body', text: 'the body', scope: 'shared' }],
185 }))
186
187 expect(await $.prompt.compose(Fixtures.COMPOSED)).toEqual({
188 sections: [
189 { id: 'heading:org', text: 'the org says hi', scope: 'shared' },
190 { id: 'body', text: 'the body', scope: 'shared' },
191 ],
192 })
193 },
194 )
195
196 test(
197 "a user plugin's rewrite of policy is skipped for every other reader",
198 {
199 plugins: [
200 Fixtures.stripping,
201 Fixtures.reading,
202 Fixtures.registering('mine'),
203 ],
204 },
205 async ($, on) => {
206 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
207
208 const registered = Fixtures.toolsRegistered(on)
209 const { text } = await $.command.run(Fixtures.POLICY_COMMAND)
210
211 await $.session.start(Fixtures.SESSION)
212
213 expect(
214 JSON.parse(text ?? 'null'),
215 "another user plugin's read sees the allowlist the stripper hides",
216 ).toEqual(Fixtures.MANAGED_POLICY)
217
218 expect(
219 registered,
220 "sec-default's own tool.register hook still reads the allowlist",
221 ).toEqual([])
222 },
223 )
224
225 test(
226 "an organization provider's subject passes over the user plugins",
227 { plugins: [Fixtures.marking] },
228 async ($, on) => {
229 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
230 Fixtures.subjectsEchoed(on)
231
232 for (const provider of Fixtures.ORG_PROVIDERS) {
233 expect(
234 await $.tool.describe(
235 Fixtures.toolDescribed('mcp__corp__search', provider),
236 ),
237 ).toEqual({ description: 'd' })
238
239 expect(
240 (
241 await $.command.describe(
242 Fixtures.commandDescribed('suite:deploy', provider),
243 )
244 ).isHidden,
245 ).toBe(false)
246
247 expect(
248 await $.agent.offer(
249 Fixtures.agentOffered('suite:reviewer', provider),
250 ),
251 ).toEqual({ isOffered: true })
252
253 expect(await $.agent.spawn(Fixtures.agentSpawned(provider))).toEqual({
254 model: 'core',
255 })
256 }
257
258 for (const provider of Fixtures.USER_REACHABLE_PROVIDERS) {
259 expect(
260 await $.tool.describe(
261 Fixtures.toolDescribed('mcp__mine__search', provider),
262 ),
263 ).toEqual({ description: 'user: d' })
264
265 expect(
266 (
267 await $.command.describe(
268 Fixtures.commandDescribed('mine:deploy', provider),
269 )
270 ).isHidden,
271 ).toBe(true)
272
273 expect(
274 await $.agent.offer(Fixtures.agentOffered('reviewer', provider)),
275 ).toEqual({ isOffered: false })
276
277 expect(await $.agent.spawn(Fixtures.agentSpawned(provider))).toEqual({
278 model: 'user',
279 })
280 }
281 },
282 )
283
284 test(
285 'an odd provider passes over the user plugins: it fails closed',
286 { plugins: [Fixtures.marking] },
287 async ($, on) => {
288 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
289 Fixtures.subjectsEchoed(on)
290
291 for (const provider of Fixtures.ODD_PROVIDERS) {
292 expect(
293 await $.tool.describe(Fixtures.toolDescribed('Bash', provider)),
294 ).toEqual({ description: 'd' })
295
296 expect(await $.agent.spawn(Fixtures.agentSpawned(provider))).toEqual({
297 model: 'core',
298 })
299 }
300 },
301 )
302
303 test(
304 'a subject decision reads no policy; a burst of listings reads once',
305 { plugins: [Fixtures.listing] },
306 async ($, on) => {
307 const reads = Fixtures.policyReads(on, Fixtures.MANAGED_POLICY)
308
309 Fixtures.subjectsEchoed(on)
310 on('tool.list', () => ({ value: [...Fixtures.TOOLS] }))
311
312 for (const provider of Fixtures.ORG_PROVIDERS) {
313 await $.tool.describe(Fixtures.toolDescribed('mcp__corp__x', provider))
314 await $.agent.spawn(Fixtures.agentSpawned(provider))
315 }
316
317 expect(reads(), "one read: the person's plugin was admitted").toBe(1)
318
319 await Promise.all([
320 $.command.run(Fixtures.TOOLS_COMMAND),
321 $.command.run(Fixtures.TOOLS_COMMAND),
322 $.command.run(Fixtures.TOOLS_COMMAND),
323 ])
324
325 expect(reads()).toBe(2)
326 },
327 )
328
329 test(
330 'the refusal a user-tier caller reads names the allowlist',
331 {
332 plugins: [
333 {
334 name: 'asking',
335 register(on) {
336 on('command.run', { command: 'greet' }, $ =>
337 $.tool
338 .register({
339 name: 'greet',
340 description: 'Says hello.',
341 inputSchema: { type: 'object' },
342 })
343 .then(
344 () => ({ text: 'registered' }),
345 (error: unknown) => ({ text: String(error) }),
346 ),
347 )
348 },
349 },
350 ],
351 },
352 async ($, on) => {
353 on('settings.read', () => ({ value: Fixtures.ALLOWLIST }))
354
355 const { text } = await $.command.run({
356 command: 'greet',
357 args: '',
358 origin: { kind: 'composer' },
359 presentation: Fixtures.FULLSCREEN,
360 })
361
362 expect(text).toEndWith(
363 `asking: $.tool.register: ${Hooks.TOOL_REGISTER_REFUSAL}`,
364 )
365 },
366 )
367
368 test(
369 "under allowManagedModsOnly a person's mod is kept out, told why",
370 { plugins: [Fixtures.registering('mine')] },
371 async ($, on) => {
372 on('settings.read', () => ({ value: Fixtures.MANAGED_MODS_ONLY }))
373
374 const registered = Fixtures.toolsRegistered(on)
375
376 await expect($.session.start(Fixtures.SESSION)).rejects.toThrow(
377 Hooks.managedModsOnlyRefusal('mine'),
378 )
379
380 expect(registered, 'no hook of it ever ran').toEqual([])
381 },
382 )
383
384 test(
385 "under it the organization's mods and the built-ins load",
386 {
387 plugins: [
388 Fixtures.registering('suite', 'prepend'),
389 Fixtures.registering('after', 'append'),
390 Fixtures.registering('bundled', 'builtin'),
391 ],
392 },
393 async ($, on) => {
394 on('settings.read', () => ({ value: Fixtures.MANAGED_MODS_ONLY }))
395
396 const registered = Fixtures.toolsRegistered(on)
397
398 await $.session.start(Fixtures.SESSION)
399
400 expect(registered).toEqual(['suite', 'after', 'bundled'])
401 },
402 )
403
404 test(
405 'set to false, a mod the person installed loads as before',
406 { plugins: [Fixtures.registering('mine')] },
407 async ($, on) => {
408 on('settings.read', () => ({ value: Fixtures.modsPolicyOf(false) }))
409
410 const registered = Fixtures.toolsRegistered(on)
411
412 await $.session.start(Fixtures.SESSION)
413
414 expect(registered).toEqual(['mine'])
415 },
416 )
417
418 test(
419 "a person's copy under an organization mod's name is still theirs",
420 { plugins: [Fixtures.registering('suite')] },
421 async ($, on) => {
422 on('settings.read', () => ({
423 value: { ...Fixtures.MANAGED_POLICY, ...Fixtures.MANAGED_MODS_ONLY },
424 }))
425
426 const registered = Fixtures.toolsRegistered(on)
427
428 await expect($.session.start(Fixtures.SESSION)).rejects.toThrow(
429 Hooks.managedModsOnlyRefusal('suite'),
430 )
431
432 expect(registered).toEqual([])
433 },
434 )
435
436 test(
437 "only managed settings are asked: a person's own cannot loosen it",
438 { plugins: [Fixtures.registering('mine')] },
439 async ($, on) => {
440 Fixtures.policyBySource(
441 on,
442 Fixtures.MANAGED_MODS_ONLY,
443 Fixtures.modsPolicyOf(false),
444 )
445
446 const registered = Fixtures.toolsRegistered(on)
447
448 await expect($.session.start(Fixtures.SESSION)).rejects.toThrow(
449 Hooks.managedModsOnlyRefusal('mine'),
450 )
451
452 expect(registered).toEqual([])
453 },
454 )
455
456 test(
457 "nor turn it on: set only in a person's settings, it is not read",
458 { plugins: [Fixtures.registering('mine')] },
459 async ($, on) => {
460 Fixtures.policyBySource(
461 on,
462 Fixtures.NO_ALLOWLIST,
463 Fixtures.MANAGED_MODS_ONLY,
464 )
465
466 const registered = Fixtures.toolsRegistered(on)
467
468 await $.session.start(Fixtures.SESSION)
469
470 expect(registered).toEqual(['mine'])
471 },
472 )
473
474 test(
475 "a policy that cannot be read keeps a person's mod out: the hook's catch",
476 { plugins: [Fixtures.registering('mine')] },
477 async ($, on) => {
478 on('settings.read', () => ({ deny: 'managed settings unreadable' }))
479
480 const lines = Fixtures.logged(on)
481 const registered = Fixtures.toolsRegistered(on)
482
483 await expect($.session.start(Fixtures.SESSION)).rejects.toThrow(
484 Hooks.managedModsOnlyRefusal('mine'),
485 )
486
487 expect({ registered, lines }).toEqual({
488 registered: [],
489 lines: [
490 'debug: plugin.register hook failed judging mine (throw): ' +
491 'sec-default: $.settings.read: managed settings unreadable',
492 ],
493 })
494 },
495 )
496})