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
GitHub Actions (publish-mods)chore: Update mods/ 3deb873 · 14 days ago History
2,215 lines · 62.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 "a plugin of theirs named as an organization's server adds no tool",
90 {
91 plugins: [
92 Fixtures.registering('corp'),
93 Fixtures.registering('mine'),
94 Fixtures.registering('bundled', 'builtin'),
95 ],
96 },
97 async ($, on) => {
98 on('settings.read', () => ({ value: Fixtures.SERVER_POLICY }))
99
100 const registered = Fixtures.toolsRegistered(on)
101
102 await $.session.start(Fixtures.SESSION)
103
104 expect(registered).toEqual(['mine', 'bundled'])
105 },
106 )
107
108 test(
109 "a plugin of theirs named as a seated plugin's own server adds no tool",
110 {
111 plugins: [
112 Fixtures.registering('plugin-corp-suite-corp-jira'),
113 Fixtures.registering('mine'),
114 ],
115 },
116 async ($, on) => {
117 on('settings.read', () => ({ value: Fixtures.SEATED_SUITE }))
118
119 const registered = Fixtures.toolsRegistered(on)
120
121 await $.session.start(Fixtures.SESSION)
122
123 expect(registered).toEqual(['mine'])
124 },
125 )
126
127 test(
128 "the organization's own plugin under such a name still adds its tool",
129 { plugins: [Fixtures.registering('corp', 'append')] },
130 async ($, on) => {
131 on('settings.read', () => ({ value: Fixtures.SERVER_POLICY }))
132
133 const registered = Fixtures.toolsRegistered(on)
134
135 await $.session.start(Fixtures.SESSION)
136
137 expect(registered).toEqual(['corp'])
138 },
139 )
140
141 test(
142 'the organization tools are listed as its tiers listed them',
143 { plugins: [Fixtures.relabeling, Fixtures.listing] },
144 async ($, on) => {
145 on('settings.read', () => ({ value: Fixtures.ALLOWLIST }))
146 on('tool.list', () => ({ value: [...Fixtures.TOOLS] }))
147
148 const { text } = await $.command.run(Fixtures.TOOLS_COMMAND)
149
150 expect(text?.split('\n')).toEqual([
151 'mcp__corp__search: Searches the corp wiki.',
152 'Bash: relabeled',
153 ])
154 },
155 )
156
157 test(
158 "a seated plugin's own server tools are listed as its tiers listed them",
159 { plugins: [Fixtures.relabeling, Fixtures.listing] },
160 async ($, on) => {
161 on('settings.read', () => ({ value: Fixtures.SEATED_SUITE }))
162 on('tool.list', () => ({ value: [...Fixtures.SUITE_TOOLS] }))
163
164 const { text } = await $.command.run(Fixtures.TOOLS_COMMAND)
165
166 expect(text?.split('\n')).toEqual([
167 'mcp__plugin_corp-suite_corp-jira__create_ticket: Creates a ticket.',
168 'Bash: relabeled',
169 ])
170 },
171 )
172
173 test(
174 'a seated plugin listed in another case has its server tools listed too',
175 { plugins: [Fixtures.relabeling, Fixtures.listing] },
176 async ($, on) => {
177 on('settings.read', () => ({
178 value: { prependPlugins: ['Corp-Suite@market'] },
179 }))
180 on('tool.list', () => ({ value: [...Fixtures.SUITE_TOOLS] }))
181
182 const { text } = await $.command.run(Fixtures.TOOLS_COMMAND)
183
184 expect(text?.split('\n')).toEqual([
185 'mcp__plugin_corp-suite_corp-jira__create_ticket: Creates a ticket.',
186 'Bash: relabeled',
187 ])
188 },
189 )
190
191 test(
192 'a plugin named as one a version-pinned entry turns on adds no tool',
193 {
194 plugins: [
195 Fixtures.registering('corp-suite'),
196 Fixtures.registering('mine'),
197 ],
198 },
199 async ($, on) => {
200 on('settings.read', () => ({
201 value: { enabledPlugins: { 'corp-suite@market': ['^1.0.0'] } },
202 }))
203
204 const registered = Fixtures.toolsRegistered(on)
205
206 await $.session.start(Fixtures.SESSION)
207
208 expect(registered).toEqual(['mine'])
209 },
210 )
211
212 test(
213 'a server policy delivers itself counts as a tool policy, unlisted',
214 { plugins: [Fixtures.relabeling, Fixtures.listing] },
215 async ($, on) => {
216 on('settings.read', () => ({ value: Fixtures.SERVER_POLICY }))
217 on('tool.list', () => ({ value: [...Fixtures.TOOLS] }))
218
219 const { text } = await $.command.run(Fixtures.TOOLS_COMMAND)
220
221 expect(text?.split('\n')).toEqual([
222 'mcp__corp__search: Searches the corp wiki.',
223 'Bash: relabeled',
224 ])
225 },
226 )
227
228 test(
229 "with no policy to read the organization's listing stands whole",
230 { plugins: [Fixtures.relabeling, Fixtures.listing] },
231 async ($, on) => {
232 const stopPolicy = Fixtures.policyUntilStopped(
233 on,
234 Fixtures.ALLOWLIST,
235 'settings unreadable',
236 )
237
238 on('prompt.section', ($, e) => ({ text: e.text }))
239 on('tool.list', () => ({ value: [...Fixtures.TOOLS] }))
240
241 await $.prompt.section(Fixtures.MEMORY)
242 stopPolicy()
243
244 const { text } = await $.command.run(Fixtures.TOOLS_COMMAND)
245
246 expect(text?.split('\n')).toEqual([
247 'mcp__corp__search: Searches the corp wiki.',
248 'Bash: Runs a command.',
249 ])
250 },
251 )
252
253 test(
254 'a prompt section passes over the plugins the person installed',
255 { plugins: [Fixtures.dropping, Fixtures.signing] },
256 async ($, on) => {
257 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
258 on('prompt.section', ($, e) => ({ text: e.text }))
259
260 expect(await $.prompt.section(Fixtures.MEMORY)).toEqual({
261 text: 'the org says hi (signed)',
262 })
263 },
264 )
265
266 test(
267 'what the engine attaches passes over the plugins the person installed',
268 { plugins: [Fixtures.droppingAttachments] },
269 async ($, on) => {
270 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
271 on('prompt.attachment', ($, e) => ({ text: e.text }))
272
273 expect(await $.prompt.attachment(Fixtures.HOOK_NOTE)).toEqual({
274 text: Fixtures.HOOK_NOTE.text,
275 })
276 },
277 )
278
279 test(
280 'a row the engine injects is kept whole over the plugins they installed',
281 { plugins: [Fixtures.blankingRows] },
282 async ($, on) => {
283 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
284
285 expect(await $.session.append(Fixtures.HOOK_CONTEXT_ROW)).toMatchObject({
286 message: { content: Fixtures.HOOK_CONTEXT_ROW.message.content },
287 })
288 expect(await $.session.append(Fixtures.REMINDER_ROW)).toMatchObject({
289 message: { content: Fixtures.REMINDER_ROW.message.content },
290 })
291 },
292 )
293
294 test(
295 'any other row is theirs to shape as before',
296 { plugins: [Fixtures.blankingRows] },
297 async ($, on) => {
298 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
299
300 expect(await $.session.append(Fixtures.PROMPT_ROW)).not.toMatchObject({
301 message: { content: Fixtures.PROMPT_ROW.message.content },
302 })
303 },
304 )
305
306 test(
307 "an organization plugin's note is not theirs to blank; their own is",
308 {
309 plugins: [
310 Fixtures.blankingRows,
311 Fixtures.noting('mine'),
312 Fixtures.noting('suite', 'append'),
313 ],
314 },
315 async ($, on) => {
316 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
317 on('session.start', ($, e) => ({ cwd: e.cwd }))
318
319 const kept: string[] = []
320
321 on('ui.log', ($, e, next) => {
322 kept.push(`${next.origin.plugin} kept ${e.text}`)
323
324 return { value: undefined }
325 })
326
327 await $.session.start(Fixtures.SESSION)
328
329 expect(kept.toSorted()).toEqual([
330 'mine kept []',
331 'suite kept [{"type":"text","text":"a note"}]',
332 ])
333 },
334 )
335
336 test(
337 "the system prompt's sections pass over the plugins the person installed",
338 { plugins: [Fixtures.emptying, Fixtures.heading] },
339 async ($, on) => {
340 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
341
342 on('prompt.compose', () => ({
343 sections: [{ id: 'body', text: 'the body', scope: 'shared' }],
344 }))
345
346 expect(await $.prompt.compose(Fixtures.COMPOSED)).toEqual({
347 sections: [
348 { id: 'heading:org', text: 'the org says hi', scope: 'shared' },
349 { id: 'body', text: 'the body', scope: 'shared' },
350 ],
351 })
352 },
353 )
354
355 test(
356 "nor does a person's plugin drop or reword a section it asked for",
357 { plugins: [Fixtures.rewording, Fixtures.heading] },
358 async ($, on) => {
359 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
360
361 on('prompt.compose', () => ({
362 sections: [{ id: 'body', text: 'the body', scope: 'shared' }],
363 }))
364
365 expect(await $.prompt.compose(Fixtures.COMPOSED)).toEqual({
366 sections: [
367 { id: 'heading:org', text: 'the org says hi', scope: 'shared' },
368 { id: 'body', text: 'the body', scope: 'shared' },
369 ],
370 })
371 },
372 )
373
374 test(
375 "a user plugin's rewrite of policy is skipped for every other reader",
376 {
377 plugins: [
378 Fixtures.stripping,
379 Fixtures.reading,
380 Fixtures.registering('mine'),
381 ],
382 },
383 async ($, on) => {
384 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
385
386 const registered = Fixtures.toolsRegistered(on)
387 const { text } = await $.command.run(Fixtures.POLICY_COMMAND)
388
389 await $.session.start(Fixtures.SESSION)
390
391 expect(
392 JSON.parse(text ?? 'null'),
393 "another user plugin's read sees the allowlist the stripper hides",
394 ).toEqual(Fixtures.MANAGED_POLICY)
395
396 expect(
397 registered,
398 "sec-default's own tool.register hook still reads the allowlist",
399 ).toEqual([])
400 },
401 )
402
403 test(
404 "an organization provider's subject passes over the user plugins",
405 { plugins: [Fixtures.marking] },
406 async ($, on) => {
407 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
408 Fixtures.subjectsEchoed(on)
409
410 for (const provider of Fixtures.ORG_PROVIDERS) {
411 expect(
412 await $.tool.describe(
413 Fixtures.toolDescribed('mcp__corp__search', provider),
414 ),
415 ).toEqual({ description: 'd' })
416
417 expect(
418 (
419 await $.command.describe(
420 Fixtures.commandDescribed('suite:deploy', provider),
421 )
422 ).isHidden,
423 ).toBe(false)
424
425 expect(
426 await $.agent.offer(
427 Fixtures.agentOffered('suite:reviewer', provider),
428 ),
429 ).toEqual({ isOffered: true })
430
431 expect(await $.agent.spawn(Fixtures.agentSpawned(provider))).toEqual({
432 model: 'core',
433 })
434 }
435
436 for (const provider of Fixtures.USER_REACHABLE_PROVIDERS) {
437 expect(
438 await $.tool.describe(
439 Fixtures.toolDescribed('mcp__mine__search', provider),
440 ),
441 ).toEqual({ description: 'user: d' })
442
443 expect(
444 (
445 await $.command.describe(
446 Fixtures.commandDescribed('mine:deploy', provider),
447 )
448 ).isHidden,
449 ).toBe(true)
450
451 expect(
452 await $.agent.offer(Fixtures.agentOffered('reviewer', provider)),
453 ).toEqual({ isOffered: false })
454
455 expect(await $.agent.spawn(Fixtures.agentSpawned(provider))).toEqual({
456 model: 'user',
457 })
458 }
459 },
460 )
461
462 test(
463 'an odd provider passes over the user plugins: it fails closed',
464 { plugins: [Fixtures.marking] },
465 async ($, on) => {
466 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
467 Fixtures.subjectsEchoed(on)
468
469 for (const provider of Fixtures.ODD_PROVIDERS) {
470 expect(
471 await $.tool.describe(Fixtures.toolDescribed('Bash', provider)),
472 ).toEqual({ description: 'd' })
473
474 expect(await $.agent.spawn(Fixtures.agentSpawned(provider))).toEqual({
475 model: 'core',
476 })
477 }
478 },
479 )
480
481 test(
482 'a subject decision reads no policy; a burst of listings reads once',
483 { plugins: [Fixtures.listing] },
484 async ($, on) => {
485 const reads = Fixtures.policyReads(on, Fixtures.MANAGED_POLICY)
486
487 Fixtures.subjectsEchoed(on)
488 on('tool.list', () => ({ value: [...Fixtures.TOOLS] }))
489
490 for (const provider of Fixtures.ORG_PROVIDERS) {
491 await $.tool.describe(Fixtures.toolDescribed('mcp__corp__x', provider))
492 await $.agent.spawn(Fixtures.agentSpawned(provider))
493 }
494
495 expect(reads(), "one read: the person's plugin was admitted").toBe(1)
496
497 await Promise.all([
498 $.command.run(Fixtures.TOOLS_COMMAND),
499 $.command.run(Fixtures.TOOLS_COMMAND),
500 $.command.run(Fixtures.TOOLS_COMMAND),
501 ])
502
503 expect(reads()).toBe(2)
504 },
505 )
506
507 test(
508 'the refusal a user-tier caller reads names the allowlist',
509 {
510 plugins: [
511 {
512 name: 'asking',
513 register(on) {
514 on('command.run', { command: 'greet' }, $ =>
515 $.tool
516 .register({
517 name: 'greet',
518 description: 'Says hello.',
519 inputSchema: { type: 'object' },
520 })
521 .then(
522 () => ({ text: 'registered' }),
523 (error: unknown) => ({ text: String(error) }),
524 ),
525 )
526 },
527 },
528 ],
529 },
530 async ($, on) => {
531 on('settings.read', () => ({ value: Fixtures.ALLOWLIST }))
532
533 const { text } = await $.command.run({
534 command: 'greet',
535 args: '',
536 origin: { kind: 'composer' },
537 presentation: Fixtures.FULLSCREEN,
538 })
539
540 expect(text).toEndWith(
541 `asking: $.tool.register: ${Hooks.TOOL_REGISTER_REFUSAL}`,
542 )
543 },
544 )
545
546 test(
547 "under allowManagedModsOnly a person's mod is kept out, told why",
548 { plugins: [Fixtures.registering('mine')] },
549 async ($, on) => {
550 on('settings.read', () => ({ value: Fixtures.MANAGED_MODS_ONLY }))
551
552 const registered = Fixtures.toolsRegistered(on)
553
554 await expect($.session.start(Fixtures.SESSION)).rejects.toThrow(
555 Hooks.managedModsOnlyRefusal('mine'),
556 )
557
558 expect(registered, 'no hook of it ever ran').toEqual([])
559 },
560 )
561
562 test(
563 "under it the organization's mods and the built-ins load",
564 {
565 plugins: [
566 Fixtures.registering('suite', 'prepend'),
567 Fixtures.registering('after', 'append'),
568 Fixtures.registering('bundled', 'builtin'),
569 ],
570 },
571 async ($, on) => {
572 on('settings.read', () => ({ value: Fixtures.MANAGED_MODS_ONLY }))
573
574 const registered = Fixtures.toolsRegistered(on)
575
576 await $.session.start(Fixtures.SESSION)
577
578 expect(registered).toEqual(['suite', 'after', 'bundled'])
579 },
580 )
581
582 test(
583 'set to false, a mod the person installed loads as before',
584 { plugins: [Fixtures.registering('mine')] },
585 async ($, on) => {
586 on('settings.read', () => ({ value: Fixtures.modsPolicyOf(false) }))
587
588 const registered = Fixtures.toolsRegistered(on)
589
590 await $.session.start(Fixtures.SESSION)
591
592 expect(registered).toEqual(['mine'])
593 },
594 )
595
596 test(
597 "a person's copy under an organization mod's name is still theirs",
598 { plugins: [Fixtures.registering('suite')] },
599 async ($, on) => {
600 on('settings.read', () => ({
601 value: { ...Fixtures.MANAGED_POLICY, ...Fixtures.MANAGED_MODS_ONLY },
602 }))
603
604 const registered = Fixtures.toolsRegistered(on)
605
606 await expect($.session.start(Fixtures.SESSION)).rejects.toThrow(
607 Hooks.managedModsOnlyRefusal('suite'),
608 )
609
610 expect(registered).toEqual([])
611 },
612 )
613
614 test(
615 "only managed settings are asked: a person's own cannot loosen it",
616 { plugins: [Fixtures.registering('mine')] },
617 async ($, on) => {
618 Fixtures.policyBySource(
619 on,
620 Fixtures.MANAGED_MODS_ONLY,
621 Fixtures.modsPolicyOf(false),
622 )
623
624 const registered = Fixtures.toolsRegistered(on)
625
626 await expect($.session.start(Fixtures.SESSION)).rejects.toThrow(
627 Hooks.managedModsOnlyRefusal('mine'),
628 )
629
630 expect(registered).toEqual([])
631 },
632 )
633
634 test(
635 "nor turn it on: set only in a person's settings, it is not read",
636 { plugins: [Fixtures.registering('mine')] },
637 async ($, on) => {
638 Fixtures.policyBySource(
639 on,
640 Fixtures.NO_ALLOWLIST,
641 Fixtures.MANAGED_MODS_ONLY,
642 )
643
644 const registered = Fixtures.toolsRegistered(on)
645
646 await $.session.start(Fixtures.SESSION)
647
648 expect(registered).toEqual(['mine'])
649 },
650 )
651
652 test(
653 "a policy that cannot be read keeps a person's mod out: the hook's catch",
654 { plugins: [Fixtures.registering('mine')] },
655 async ($, on) => {
656 on('settings.read', () => ({ deny: 'managed settings unreadable' }))
657
658 const lines = Fixtures.logged(on)
659 const registered = Fixtures.toolsRegistered(on)
660
661 await expect($.session.start(Fixtures.SESSION)).rejects.toThrow(
662 Hooks.managedModsOnlyRefusal('mine'),
663 )
664
665 expect({ registered, lines }).toEqual({
666 registered: [],
667 lines: [
668 'debug: plugin.register hook failed judging mine (throw): ' +
669 'sec-default: $.settings.read: managed settings unreadable',
670 ],
671 })
672 },
673 )
674
675 test(
676 'a deny rule holds over an allow from a plugin the person installed',
677 { plugins: [Fixtures.allowing('easy')] },
678 async ($, on) => {
679 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
680
681 const lines = Fixtures.logged(on)
682
683 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
684
685 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
686
687 expect(lines).toEqual([
688 `transcript: ${Hooks.heldNotice('easy', 'Bash', Fixtures.RULE_DENY, 'lift')}`,
689 ])
690 },
691 )
692
693 test(
694 'two plugins of the person chained are each named once in a session',
695 {
696 plugins: [Fixtures.allowing('first'), Fixtures.asking('second')],
697 },
698 async ($, on) => {
699 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
700
701 const lines = Fixtures.logged(on)
702
703 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
704
705 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
706 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
707
708 expect(lines.toSorted()).toEqual([
709 `transcript: ${Hooks.heldNotice('first', 'Bash', Fixtures.RULE_DENY, 'lift')}`,
710 `transcript: ${Hooks.heldNotice('second', 'Bash', Fixtures.RULE_DENY, 'lift')}`,
711 ])
712 },
713 )
714
715 test(
716 'a deny rule turned into an ask by a plugin of the person holds too',
717 { plugins: [Fixtures.asking('easy')] },
718 async ($, on) => {
719 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
720 Fixtures.logged(on)
721 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
722
723 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
724 },
725 )
726
727 test(
728 "an organization plugin's allow over a deny rule stands, above or below",
729 {
730 plugins: [
731 Fixtures.allowing('suite', 'append'),
732 Fixtures.allowing('easy'),
733 ],
734 },
735 async ($, on) => {
736 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
737
738 const lines = Fixtures.logged(on)
739
740 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
741
742 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ALLOWED)
743 expect(lines).toEqual([])
744 },
745 )
746
747 test(
748 "a prepended organization plugin's allow over a deny rule stands",
749 { plugins: [Fixtures.allowing('guard', 'prepend')] },
750 async ($, on) => {
751 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
752
753 const lines = Fixtures.logged(on)
754
755 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
756
757 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ALLOWED)
758 expect(lines).toEqual([])
759 },
760 )
761
762 test(
763 "a built-in's allow over a deny rule stands",
764 { plugins: [Fixtures.allowing('bundled', 'builtin')] },
765 async ($, on) => {
766 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
767
768 const lines = Fixtures.logged(on)
769
770 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
771
772 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ALLOWED)
773 expect(lines).toEqual([])
774 },
775 )
776
777 test(
778 'an ask rule holds over an allow from a plugin the person installed',
779 { plugins: [Fixtures.allowing('easy')] },
780 async ($, on) => {
781 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
782
783 const lines = Fixtures.logged(on)
784
785 Fixtures.checksAnswered(on, Fixtures.RULE_ASK)
786
787 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_ASK)
788
789 expect(lines).toEqual([
790 `transcript: ${Hooks.heldNotice('easy', 'Bash', Fixtures.RULE_ASK, 'lift')}`,
791 ])
792 },
793 )
794
795 test(
796 "a classic hook's ask holds over an allow from a plugin of the person",
797 { plugins: [Fixtures.allowing('easy')] },
798 async ($, on) => {
799 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
800
801 const lines = Fixtures.logged(on)
802
803 Fixtures.checksAnswered(on, Fixtures.HOOK_ASK)
804
805 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.HOOK_ASK)
806
807 expect(lines).toEqual([
808 `transcript: ${Hooks.heldNotice('easy', 'Bash', Fixtures.HOOK_ASK, 'lift')}`,
809 ])
810 },
811 )
812
813 test(
814 "a classic hook's deny holds over their allow",
815 { plugins: [Fixtures.allowing('easy')] },
816 async ($, on) => {
817 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
818
819 const lines = Fixtures.logged(on)
820
821 Fixtures.checksAnswered(on, Fixtures.HOOK_DENY)
822
823 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.HOOK_DENY)
824
825 expect(lines).toEqual([
826 `transcript: ${Hooks.heldNotice('easy', 'Bash', Fixtures.HOOK_DENY, 'lift')}`,
827 ])
828 },
829 )
830
831 test(
832 "a classic hook's deny holds over their ask",
833 { plugins: [Fixtures.asking('easy')] },
834 async ($, on) => {
835 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
836 Fixtures.logged(on)
837 Fixtures.checksAnswered(on, Fixtures.HOOK_DENY)
838
839 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.HOOK_DENY)
840 },
841 )
842
843 test(
844 'an allow that never called next meets the ask rule all the same',
845 { plugins: [Fixtures.blindAllowing] },
846 async ($, on) => {
847 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
848 Fixtures.logged(on)
849
850 const evaluations = Fixtures.checksAnswered(on, Fixtures.RULE_ASK)
851
852 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_ASK)
853 expect(evaluations()).toBe(1)
854 },
855 )
856
857 test(
858 "an organization plugin's allow over an ask rule stands",
859 {
860 plugins: [
861 Fixtures.allowing('suite', 'append'),
862 Fixtures.allowing('easy'),
863 ],
864 },
865 async ($, on) => {
866 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
867
868 const lines = Fixtures.logged(on)
869
870 Fixtures.checksAnswered(on, Fixtures.RULE_ASK)
871
872 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ALLOWED)
873 expect(lines).toEqual([])
874 },
875 )
876
877 test(
878 "an ask of theirs over an ask rule is the rule's ask again, whole",
879 { plugins: [Fixtures.asking('easy')] },
880 async ($, on) => {
881 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
882
883 const lines = Fixtures.logged(on)
884
885 Fixtures.checksAnswered(on, Fixtures.RULE_ASK)
886
887 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_ASK)
888
889 expect(lines).toEqual([
890 'transcript: ' +
891 Hooks.heldNotice('easy', 'Bash', Fixtures.RULE_ASK, 'reword'),
892 ])
893 },
894 )
895
896 test(
897 "a deny of theirs over a deny rule is the rule's deny again, whole",
898 { plugins: [Fixtures.tightening] },
899 async ($, on) => {
900 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
901
902 const lines = Fixtures.logged(on)
903
904 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
905
906 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
907
908 expect(lines).toEqual([
909 'transcript: ' +
910 Hooks.heldNotice('strict', 'Bash', Fixtures.RULE_DENY, 'reword'),
911 ])
912 },
913 )
914
915 test(
916 'an ask of theirs that says what it heard costs no second look',
917 { plugins: [Fixtures.asking('easy')] },
918 async ($, on) => {
919 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
920
921 const lines = Fixtures.logged(on)
922 const evaluations = Fixtures.checksAnswered(on, Fixtures.ASKED)
923
924 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ASKED)
925 expect({ lines, evaluations: evaluations() }).toEqual({
926 lines: [],
927 evaluations: 1,
928 })
929 },
930 )
931
932 test(
933 'the option that lets them override deny rules lets them override these',
934 { plugins: [Fixtures.allowing('easy')] },
935 async ($, on) => {
936 on('settings.read', () => ({ value: Fixtures.overridePolicyOf(true) }))
937
938 const lines = Fixtures.logged(on)
939
940 Fixtures.checksAnswered(on, Fixtures.RULE_ASK)
941
942 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ALLOWED)
943 expect(lines).toEqual([])
944 },
945 )
946
947 test(
948 "the mode's own ask, no rule or hook behind it, is theirs to allow",
949 { plugins: [Fixtures.allowing('easy')] },
950 async ($, on) => {
951 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
952
953 const lines = Fixtures.logged(on)
954
955 Fixtures.checksAnswered(on, Fixtures.ASKED)
956
957 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ALLOWED)
958 expect(lines).toEqual([])
959 },
960 )
961
962 test(
963 'a deny holds whatever decided it: it may stand before a rule or a hook',
964 { plugins: [Fixtures.allowing('easy')] },
965 async ($, on) => {
966 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
967
968 const lines = Fixtures.logged(on)
969
970 Fixtures.checksAnswered(on, Fixtures.PLAIN_DENY)
971
972 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.PLAIN_DENY)
973
974 expect(lines).toEqual([
975 `transcript: ${Hooks.heldNotice('easy', 'Bash', Fixtures.PLAIN_DENY, 'lift')}`,
976 ])
977 },
978 )
979
980 test(
981 'the notice is no door: a plugin of theirs never runs inside this hook',
982 { plugins: [Fixtures.sneaking] },
983 async ($, on) => {
984 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
985
986 const lines = Fixtures.logged(on)
987
988 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
989
990 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
991
992 expect(lines).toEqual([
993 'transcript: ' +
994 Hooks.heldNotice('sneaking', 'Bash', Fixtures.RULE_DENY, 'lift'),
995 ])
996 },
997 )
998
999 test(
1000 'nor is the notice theirs to drop',
1001 { plugins: [Fixtures.muting] },
1002 async ($, on) => {
1003 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1004
1005 const lines = Fixtures.logged(on)
1006
1007 Fixtures.checksAnswered(on, Fixtures.RULE_ASK)
1008
1009 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_ASK)
1010
1011 expect(lines).toEqual([
1012 `transcript: ${Hooks.heldNotice('muting', 'Bash', Fixtures.RULE_ASK, 'lift')}`,
1013 ])
1014 },
1015 )
1016
1017 test(
1018 "an organization's line is not theirs to drop; a line of their own is",
1019 {
1020 plugins: [
1021 Fixtures.muting,
1022 Fixtures.logging('mine'),
1023 Fixtures.logging('bundled', 'builtin'),
1024 Fixtures.logging('suite', 'append'),
1025 ],
1026 },
1027 async ($, on) => {
1028 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1029 on('session.start', ($, e) => ({ cwd: e.cwd }))
1030
1031 const heard: string[] = []
1032
1033 on('ui.log', ($, e, next) => {
1034 heard.push(next.origin.plugin)
1035
1036 return { value: undefined }
1037 })
1038
1039 await $.session.start(Fixtures.SESSION)
1040
1041 expect(heard).toEqual(['suite'])
1042 },
1043 )
1044
1045 test(
1046 'each plugin is told of a kind, by the tool it answered on',
1047 { plugins: [Fixtures.allowingBash, Fixtures.allowingRead] },
1048 async ($, on) => {
1049 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1050
1051 const lines = Fixtures.logged(on)
1052
1053 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
1054
1055 await $.tool.check(Fixtures.CHECKED)
1056 await $.tool.check(Fixtures.CHECKED_READ)
1057
1058 expect(lines).toEqual([
1059 `transcript: ${Hooks.heldNotice('basher', 'Bash', Fixtures.RULE_DENY, 'lift')}`,
1060 `transcript: ${Hooks.heldNotice('reader', 'Read', Fixtures.RULE_DENY, 'lift')}`,
1061 ])
1062 },
1063 )
1064
1065 test(
1066 "an organization's appended plugin has its say in the run past theirs",
1067 {
1068 plugins: [Fixtures.allowing('suite', 'append'), Fixtures.blindAllowing],
1069 },
1070 async ($, on) => {
1071 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1072
1073 const lines = Fixtures.logged(on)
1074
1075 Fixtures.checksAnswered(on, Fixtures.RULE_ASK)
1076
1077 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ALLOWED)
1078 expect(lines).toEqual([])
1079 },
1080 )
1081
1082 test(
1083 'a plugin is told once of each kind of thing that held over it',
1084 { plugins: [Fixtures.allowing('easy')] },
1085 async ($, on) => {
1086 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1087
1088 const lines = Fixtures.logged(on)
1089 const verdicts = [
1090 Fixtures.RULE_DENY,
1091 Fixtures.RULE_ASK,
1092 Fixtures.RULE_ASK,
1093 Fixtures.HOOK_ASK,
1094 Fixtures.PLAIN_DENY,
1095 Fixtures.RULE_DENY,
1096 ]
1097 const turn = Fixtures.checksAnsweredInTurn(on, verdicts)
1098
1099 for (const verdict of verdicts) {
1100 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(verdict)
1101 turn()
1102 }
1103
1104 expect(lines).toEqual(
1105 [
1106 Fixtures.RULE_DENY,
1107 Fixtures.RULE_ASK,
1108 Fixtures.HOOK_ASK,
1109 Fixtures.PLAIN_DENY,
1110 ].map(
1111 held =>
1112 `transcript: ${Hooks.heldNotice('easy', 'Bash', held, 'lift')}`,
1113 ),
1114 )
1115 },
1116 )
1117
1118 test(
1119 'a plugin of the person that tightens is heard, with one evaluation',
1120 { plugins: [Fixtures.tightening] },
1121 async ($, on) => {
1122 const reads = Fixtures.policyReads(on, Fixtures.MANAGED_POLICY)
1123 const evaluations = Fixtures.checksAnswered(on, Fixtures.ASKED)
1124
1125 await $.tool.check(Fixtures.CHECKED)
1126
1127 const loaded = { reads: reads(), evaluations: evaluations() }
1128
1129 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.PLAIN_DENY)
1130
1131 expect(
1132 {
1133 reads: reads() - loaded.reads,
1134 evaluations: evaluations() - loaded.evaluations,
1135 },
1136 'once its plugins have loaded, a check reads no policy',
1137 ).toEqual({ reads: 0, evaluations: 1 })
1138 },
1139 )
1140
1141 test(
1142 "a deny rule holds when an organization's plugin listens above theirs",
1143 {
1144 plugins: [
1145 Fixtures.listening('audit', 'prepend'),
1146 Fixtures.allowing('easy'),
1147 ],
1148 },
1149 async ($, on) => {
1150 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1151
1152 const lines = Fixtures.logged(on)
1153
1154 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
1155
1156 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
1157
1158 expect(
1159 lines.map(line => line.includes('easy')),
1160 'one line, naming the plugin of theirs, alone or in its batch',
1161 ).toEqual([true])
1162 },
1163 )
1164
1165 test(
1166 'a plugin of the person that only listens costs no second evaluation',
1167 { plugins: [Fixtures.listening('listening')] },
1168 async ($, on) => {
1169 const reads = Fixtures.policyReads(on, Fixtures.MANAGED_POLICY)
1170 const evaluations = Fixtures.checksAnswered(on, Fixtures.ASKED)
1171
1172 await $.tool.check(Fixtures.CHECKED)
1173
1174 const loaded = { reads: reads(), evaluations: evaluations() }
1175
1176 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ASKED)
1177
1178 expect(
1179 {
1180 reads: reads() - loaded.reads,
1181 evaluations: evaluations() - loaded.evaluations,
1182 },
1183 'once its plugins have loaded, a check reads no policy',
1184 ).toEqual({ reads: 0, evaluations: 1 })
1185 },
1186 )
1187
1188 test('with none of their plugins the verdict passes once', async ($, on) => {
1189 const reads = Fixtures.policyReads(on, Fixtures.MANAGED_POLICY)
1190 const evaluations = Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
1191
1192 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
1193
1194 expect({ reads: reads(), evaluations: evaluations() }).toEqual({
1195 reads: 0,
1196 evaluations: 1,
1197 })
1198 })
1199
1200 test(
1201 'an allow that never called next meets the deny rule all the same',
1202 { plugins: [Fixtures.blindAllowing] },
1203 async ($, on) => {
1204 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1205
1206 const lines = Fixtures.logged(on)
1207 const evaluations = Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
1208
1209 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
1210
1211 expect({ lines, evaluations: evaluations() }).toEqual({
1212 lines: [
1213 'transcript: ' +
1214 Hooks.heldNotice('blind', 'Bash', Fixtures.RULE_DENY, 'lift'),
1215 ],
1216 evaluations: 1,
1217 })
1218 },
1219 )
1220
1221 test(
1222 'a rule a plugin of the person writes into its answer is never read',
1223 { plugins: [Fixtures.forging] },
1224 async ($, on) => {
1225 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1226
1227 const lines = Fixtures.logged(on)
1228
1229 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
1230
1231 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
1232
1233 expect(lines).toEqual([
1234 'transcript: ' +
1235 Hooks.heldNotice('forging', 'Bash', Fixtures.RULE_DENY, 'lift'),
1236 ])
1237 },
1238 )
1239
1240 test(
1241 'a plugin of the person asking about another command is left out',
1242 { plugins: [Fixtures.rewriting] },
1243 async ($, on) => {
1244 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1245 Fixtures.logged(on)
1246
1247 const asked: unknown[] = []
1248
1249 on('tool.check', ($, e) => {
1250 asked.push(e.input)
1251
1252 return Fixtures.RULE_DENY
1253 })
1254
1255 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
1256
1257 expect(
1258 asked,
1259 'the rules were asked about the command that would run, only',
1260 ).toEqual([Fixtures.CHECKED.input])
1261 },
1262 )
1263
1264 test(
1265 'managed settings may let the plugins a person installs override',
1266 { plugins: [Fixtures.allowing('easy')] },
1267 async ($, on) => {
1268 on('settings.read', () => ({ value: Fixtures.overridePolicyOf(true) }))
1269
1270 const lines = Fixtures.logged(on)
1271 const evaluations = Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
1272
1273 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ALLOWED)
1274
1275 expect({ lines, evaluations: evaluations() }).toEqual({
1276 lines: [],
1277 evaluations: 1,
1278 })
1279 },
1280 )
1281
1282 test(
1283 "only managed settings are asked: a person's own cannot lift a deny rule",
1284 { plugins: [Fixtures.allowing('easy')] },
1285 async ($, on) => {
1286 Fixtures.policyBySource(
1287 on,
1288 Fixtures.MANAGED_POLICY,
1289 Fixtures.overridePolicyOf(true),
1290 )
1291
1292 Fixtures.logged(on)
1293 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
1294
1295 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
1296 },
1297 )
1298
1299 test(
1300 'the option mistyped lifts nothing: only the literal true does',
1301 { plugins: [Fixtures.allowing('easy')] },
1302 async ($, on) => {
1303 on('settings.read', () => ({ value: Fixtures.overridePolicyOf('true') }))
1304 Fixtures.logged(on)
1305 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
1306
1307 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
1308 },
1309 )
1310
1311 test(
1312 'with a policy that cannot be read the deny rule holds: fails closed',
1313 { plugins: [Fixtures.allowing('easy')] },
1314 async ($, on) => {
1315 const stopPolicy = Fixtures.policyUntilStopped(
1316 on,
1317 Fixtures.overridePolicyOf(true),
1318 'managed settings unreadable',
1319 )
1320
1321 Fixtures.logged(on)
1322 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
1323 on('prompt.section', ($, e) => ({ text: e.text }))
1324
1325 await $.prompt.section(Fixtures.MEMORY)
1326 stopPolicy()
1327
1328 expect(
1329 await $.tool.check(Fixtures.CHECKED),
1330 'the policy that read let plugins override; unread, the rule holds',
1331 ).toEqual(Fixtures.RULE_DENY)
1332 },
1333 )
1334
1335 test(
1336 "when the rules cannot be evaluated the call is refused: the hook's catch",
1337 { plugins: [Fixtures.blindAllowing] },
1338 async ($, on) => {
1339 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1340 Fixtures.logged(on)
1341
1342 on('tool.check', () => {
1343 throw new Error('the evaluation failed')
1344 })
1345
1346 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Hooks.UNCHECKED_DENY)
1347 },
1348 )
1349
1350 test('the refusal of a pinned variable names the setting and it', () => {
1351 expect(Hooks.pinnedVariableRefusal('CORP_PROXY')).toBe(
1352 'env (managed): CORP_PROXY is not for plugins outside policy to change',
1353 )
1354 })
1355
1356 test('a name managed env sets is pinned, in any case; no other is', () => {
1357 expect(
1358 ['CORP_PROXY', 'corp_proxy', 'Corp_Proxy', 'CORP', 'CORP_PROXY_2'].map(
1359 name => Hooks.isPinnedVariable(Fixtures.PINNING_POLICY, name),
1360 ),
1361 ).toEqual([true, true, true, false, false])
1362 })
1363
1364 test('whichever case managed env itself spells it in', () => {
1365 expect(
1366 ['CORP_PROXY', 'corp_proxy', 'CORP'].map(name =>
1367 Hooks.isPinnedVariable(Fixtures.LOWER_CASE_PINNING_POLICY, name),
1368 ),
1369 ).toEqual([true, true, false])
1370 })
1371
1372 test('a name managed env sets to nothing is pinned all the same', () => {
1373 expect(
1374 Hooks.isPinnedVariable({ env: { CORP_PROXY: '' } }, 'CORP_PROXY'),
1375 ).toBe(true)
1376 })
1377
1378 test('settings with no env pin nothing', () => {
1379 expect(
1380 [Fixtures.MANAGED_POLICY, {}, { env: {} }].map(policy =>
1381 Hooks.isPinnedVariable(policy, 'CORP_PROXY'),
1382 ),
1383 ).toEqual([false, false, false])
1384 })
1385
1386 test(
1387 'a variable managed settings pin is not for a plugin of the person to set',
1388 {
1389 plugins: [
1390 Fixtures.settingProxy('suite', 'prepend'),
1391 Fixtures.settingProxy('mine'),
1392 Fixtures.settingProxy('bundled', 'builtin'),
1393 ],
1394 },
1395 async ($, on) => {
1396 on('settings.read', () => ({ value: Fixtures.PINNING_POLICY }))
1397
1398 const lines = Fixtures.logged(on)
1399 const set = Fixtures.variablesSet(on)
1400
1401 await $.session.start(Fixtures.SESSION)
1402
1403 expect(set).toEqual(['suite CORP_PROXY=mine', 'bundled CORP_PROXY=mine'])
1404 expect(lines).toHaveLength(1)
1405 expect(lines[0]).toContain(Hooks.pinnedVariableRefusal('CORP_PROXY'))
1406 },
1407 )
1408
1409 test(
1410 'nor to unset',
1411 { plugins: [Fixtures.unsettingProxy] },
1412 async ($, on) => {
1413 on('settings.read', () => ({ value: Fixtures.PINNING_POLICY }))
1414
1415 const set = Fixtures.variablesSet(on)
1416
1417 await $.session.start(Fixtures.SESSION)
1418
1419 expect(set).toEqual([])
1420 },
1421 )
1422
1423 test(
1424 'nor under another case, which some systems read as the same name',
1425 { plugins: [Fixtures.settingLowerCase] },
1426 async ($, on) => {
1427 on('settings.read', () => ({ value: Fixtures.PINNING_POLICY }))
1428 Fixtures.logged(on)
1429
1430 const set = Fixtures.variablesSet(on)
1431
1432 await $.session.start(Fixtures.SESSION)
1433
1434 expect(set).toEqual([])
1435 },
1436 )
1437
1438 test(
1439 "what an organization's plugin reads is not theirs to answer",
1440 {
1441 plugins: [
1442 Fixtures.answeringReads,
1443 Fixtures.readingProxy('mine'),
1444 Fixtures.readingProxy('suite', 'append'),
1445 Fixtures.readingProxy('guard', 'prepend'),
1446 ],
1447 },
1448 async ($, on) => {
1449 on('settings.read', () => ({ value: Fixtures.PINNING_POLICY }))
1450 on('session.start', ($, e) => ({ cwd: e.cwd }))
1451 on('env.get', () => ({ value: 'as the engine holds it' }))
1452
1453 const lines = Fixtures.loggedBy(on)
1454
1455 await $.session.start(Fixtures.SESSION)
1456
1457 expect(lines.toSorted()).toEqual([
1458 'guard read as the engine holds it',
1459 'mine read theirs',
1460 'suite read as the engine holds it',
1461 ])
1462 },
1463 )
1464
1465 test(
1466 "nor to rewrite what an organization's plugin sets, pinned or not",
1467 {
1468 plugins: [
1469 Fixtures.revaluing,
1470 Fixtures.settingProxy('suite', 'append'),
1471 Fixtures.settingOther('audit', 'append'),
1472 Fixtures.settingOther('guard', 'prepend'),
1473 ],
1474 },
1475 async ($, on) => {
1476 on('settings.read', () => ({ value: Fixtures.PINNING_POLICY }))
1477
1478 const set = Fixtures.variablesSet(on)
1479
1480 await $.session.start(Fixtures.SESSION)
1481
1482 expect(set.toSorted()).toEqual([
1483 'audit OTHER=mine',
1484 'guard OTHER=mine',
1485 'suite CORP_PROXY=mine',
1486 ])
1487 },
1488 )
1489
1490 test(
1491 "an organization's write waits on no policy read, so none can fail it",
1492 {
1493 plugins: [Fixtures.revaluing, Fixtures.settingOther('audit', 'append')],
1494 },
1495 async ($, on) => {
1496 const stopPolicy = Fixtures.policyUntilStopped(
1497 on,
1498 Fixtures.MANAGED_POLICY,
1499 'managed settings unreadable',
1500 )
1501
1502 Fixtures.logged(on)
1503
1504 on('prompt.section', ($, e) => ({ text: e.text }))
1505
1506 const set = Fixtures.variablesSet(on)
1507
1508 await $.prompt.section(Fixtures.MEMORY)
1509 stopPolicy()
1510 await $.session.start(Fixtures.SESSION)
1511
1512 expect(set).toEqual(['audit OTHER=mine'])
1513 },
1514 )
1515
1516 test(
1517 "a built-in's write of a variable nobody pins still passes their hooks",
1518 {
1519 plugins: [
1520 Fixtures.revaluing,
1521 Fixtures.settingOther('bundled', 'builtin'),
1522 ],
1523 },
1524 async ($, on) => {
1525 on('settings.read', () => ({ value: Fixtures.PINNING_POLICY }))
1526
1527 const set = Fixtures.variablesSet(on)
1528
1529 await $.session.start(Fixtures.SESSION)
1530
1531 expect(set).toEqual(['bundled OTHER=theirs'])
1532 },
1533 )
1534
1535 test(
1536 'the option that lets them override deny rules unpins nothing',
1537 { plugins: [Fixtures.settingProxy('mine')] },
1538 async ($, on) => {
1539 on('settings.read', () => ({
1540 value: {
1541 ...Fixtures.overridePolicyOf(true),
1542 ...Fixtures.PINNING_POLICY,
1543 },
1544 }))
1545 Fixtures.logged(on)
1546
1547 const set = Fixtures.variablesSet(on)
1548
1549 await $.session.start(Fixtures.SESSION)
1550
1551 expect(set).toEqual([])
1552 },
1553 )
1554
1555 test(
1556 'a variable managed settings do not pin is theirs to set',
1557 { plugins: [Fixtures.settingOther('mine')] },
1558 async ($, on) => {
1559 on('settings.read', () => ({ value: Fixtures.PINNING_POLICY }))
1560
1561 const set = Fixtures.variablesSet(on)
1562
1563 await $.session.start(Fixtures.SESSION)
1564
1565 expect(set).toEqual(['mine OTHER=mine'])
1566 },
1567 )
1568
1569 test(
1570 "only managed settings pin: a person's own env binds no plugin",
1571 { plugins: [Fixtures.settingProxy('mine')] },
1572 async ($, on) => {
1573 Fixtures.policyBySource(
1574 on,
1575 Fixtures.MANAGED_POLICY,
1576 Fixtures.PINNING_POLICY,
1577 )
1578
1579 const set = Fixtures.variablesSet(on)
1580
1581 await $.session.start(Fixtures.SESSION)
1582
1583 expect(set).toEqual(['mine CORP_PROXY=mine'])
1584 },
1585 )
1586
1587 test(
1588 'with a policy that cannot be read they set no variable: fails closed',
1589 { plugins: [Fixtures.settingOther('mine')] },
1590 async ($, on) => {
1591 const stopPolicy = Fixtures.policyUntilStopped(
1592 on,
1593 Fixtures.MANAGED_POLICY,
1594 'managed settings unreadable',
1595 )
1596
1597 Fixtures.logged(on)
1598
1599 on('prompt.section', ($, e) => ({ text: e.text }))
1600
1601 const set = Fixtures.variablesSet(on)
1602
1603 await $.prompt.section(Fixtures.MEMORY)
1604 stopPolicy()
1605 await $.session.start(Fixtures.SESSION)
1606
1607 expect(set).toEqual([])
1608 },
1609 )
1610
1611 test("a server's own name, and any name under its tools' prefix", () => {
1612 expect(
1613 ['corp', 'corp__x', 'corp__x__y', 'corp._x'].map(plugin =>
1614 Hooks.namesOrgTool(Fixtures.SERVER_POLICY, plugin, 'greet'),
1615 ),
1616 ).toEqual([true, true, true, true])
1617 })
1618
1619 test('a name that only begins like one is no server of theirs', () => {
1620 expect(
1621 ['corporate', 'corpx', 'x__corp', 'x-corp', 'mine'].map(plugin =>
1622 Hooks.namesOrgTool(Fixtures.SERVER_POLICY, plugin, 'greet'),
1623 ),
1624 ).toEqual([false, false, false, false, false])
1625 })
1626
1627 test('another case or other separators spell the same server', () => {
1628 expect(
1629 ['Corp', 'CORP', 'corp_x', 'corp-x', 'corp.x', '_corp_'].map(plugin =>
1630 Hooks.namesOrgTool(Fixtures.SERVER_POLICY, plugin, 'greet'),
1631 ),
1632 ).toEqual([true, true, true, true, true, true])
1633 })
1634
1635 test('a claude.ai server with a run of spaces folds as its tools do', () => {
1636 expect(
1637 ['mcp__claude_ai_tools__x', 'mcp__claude_ai__tools__x'].map(tool =>
1638 Hooks.isOrgTool(
1639 { managedMcpServers: { 'claude.ai tools': {} } },
1640 tool,
1641 ),
1642 ),
1643 ).toEqual([true, true])
1644 })
1645
1646 test('a plugin and its tool that spell a server together count too', () => {
1647 expect(
1648 [
1649 ['corp', 'prod__deploy'],
1650 ['corp', 'deploy'],
1651 ['corp_tools', 'greet'],
1652 ['corp', 'tools__greet'],
1653 ].map(([plugin = '', tool = '']) =>
1654 Hooks.namesOrgTool(
1655 { managedMcpServers: { corp__prod: {}, 'corp.tools': {} } },
1656 plugin,
1657 tool,
1658 ),
1659 ),
1660 ).toEqual([true, false, true, true])
1661 })
1662
1663 test("a seated plugin's own servers are the organization's", () => {
1664 expect([
1665 Hooks.namesOrgTool(
1666 Fixtures.SEATED_SUITE,
1667 'plugin_corp-suite_corp-jira',
1668 'create_ticket',
1669 ),
1670 Hooks.namesOrgTool(Fixtures.SEATED_SUITE, 'Plugin.Corp_Suite.x', 'y'),
1671 Hooks.isOrgTool(
1672 Fixtures.SEATED_SUITE,
1673 'mcp__plugin_corp-suite_corp-jira__create_ticket',
1674 ),
1675 ]).toEqual([true, true, true])
1676 })
1677
1678 test('a seated plugin is told in any case, as the engine seats it', () => {
1679 const listed = { prependPlugins: ['Corp-Suite@market'] }
1680
1681 expect([
1682 Hooks.isOrgTool(listed, 'mcp__plugin_corp-suite_corp-jira__x'),
1683 Hooks.isOrgTool(listed, 'mcp__corp-suite__greet'),
1684 Hooks.isOrgTool(Fixtures.SEATED_SUITE, 'mcp__plugin_Corp-Suite_j__x'),
1685 Hooks.isOrgTool(listed, 'mcp__plugin_corp-suites_j__x'),
1686 Hooks.isOrgTool(Fixtures.SERVER_POLICY, 'mcp__Corp__search'),
1687 ]).toEqual([true, true, true, false, false])
1688 })
1689
1690 test('an entry that pins versions turns its plugin on, as true does', () => {
1691 const pinned = { enabledPlugins: { 'corp-suite@market': ['^1.0.0'] } }
1692 const off = { enabledPlugins: { 'corp-suite@market': false } }
1693
1694 expect([
1695 Hooks.namesOrgTool(pinned, 'corp-suite', 'greet'),
1696 Hooks.isOrgTool(pinned, 'mcp__corp-suite__greet'),
1697 Hooks.namesOrgTool(off, 'corp-suite', 'greet'),
1698 Hooks.namesOrgTool(pinned, 'mine', 'greet'),
1699 ]).toEqual([true, true, false, false])
1700 })
1701
1702 test('a name in the other normalization form is the same name', () => {
1703 const policy = { prependPlugins: ['caf\u00e9-suite@market'] }
1704
1705 expect([
1706 Hooks.namesOrgTool(policy, 'cafe\u0301-suite', 'x'),
1707 Hooks.isOrgTool(policy, 'mcp__plugin_cafe\u0301-suite_jira__x'),
1708 Hooks.isOrgTool(policy, 'mcp__cafe\u0301-suite__x'),
1709 ]).toEqual([true, true, true])
1710 })
1711
1712 test('only the id managed settings turn on keeps its own name', () => {
1713 const policy = {
1714 enabledPlugins: { 'corp-guard@corp-market': true },
1715 appendPlugins: ['corp-guard@corp-market'],
1716 }
1717 const asked = (id?: string) =>
1718 Hooks.namesOrgTool(policy, 'corp-guard', 'x', id)
1719
1720 expect([
1721 asked('corp-guard@corp-market'),
1722 asked('Corp-Guard@Corp-Market'),
1723 asked('corp-guard@inline'),
1724 asked('corp-guard@elsewhere'),
1725 asked('corp_guard@corp-market'),
1726 asked(),
1727 Hooks.namesOrgTool(
1728 { ...policy, managedMcpServersUnreadable: true },
1729 'corp-guard',
1730 'x',
1731 'corp-guard@corp-market',
1732 ),
1733 Hooks.namesOrgTool({}, 'corp-guard', 'x', 'corp-guard@inline'),
1734 ]).toEqual([false, false, true, true, true, true, true, false])
1735 })
1736
1737 test('an accented name is told as the engine spells it in a tool', () => {
1738 const composed = { prependPlugins: ['caf\u00e9-suite@market'] }
1739 const decomposed = { prependPlugins: ['cafe\u0301-suite@market'] }
1740
1741 expect([
1742 Hooks.isOrgTool(composed, 'mcp__caf_-suite__x'),
1743 Hooks.isOrgTool(composed, 'mcp__cafe_-suite__x'),
1744 Hooks.isOrgTool(decomposed, 'mcp__caf_-suite__x'),
1745 Hooks.isOrgTool(decomposed, 'mcp__cafe_-suite__x'),
1746 Hooks.isOrgTool(composed, 'mcp__plugin_caf_-suite_jira__x'),
1747 Hooks.namesOrgTool(composed, 'caf.-suite', 'x'),
1748 Hooks.namesOrgTool(composed, 'cafe\u0301-suite', 'x'),
1749 Hooks.namesOrgTool(decomposed, 'caf\u00e9-suite', 'x'),
1750 ]).toEqual([true, true, true, true, true, true, true, true])
1751 })
1752
1753 test('a combining mark on a plain letter does not part a name', () => {
1754 const decomposed = { prependPlugins: ['cafe\u0301-suite@market'] }
1755
1756 expect([
1757 Hooks.namesOrgTool(Fixtures.SEATED_SUITE, 'corp-suite\u0301', 'x'),
1758 Hooks.isOrgTool(
1759 Fixtures.SEATED_SUITE,
1760 'mcp__plugin_corp-suite\u0301_jira__x',
1761 ),
1762 Hooks.namesOrgTool(decomposed, 'cafe\u0301-suite', 'x'),
1763 Hooks.isOrgTool(decomposed, 'mcp__cafe\u0301-suite__x'),
1764 ]).toEqual([true, true, true, true])
1765 })
1766
1767 test('a name in plain letters folds exactly as before', () => {
1768 expect(
1769 ['Corp__Tools', 'corp-tools', 'corp.tools', 'CORP TOOLS'].map(
1770 Hooks.toolPrefixOf,
1771 ),
1772 ).toEqual([
1773 'mcp_corp_tools_',
1774 'mcp_corp_tools_',
1775 'mcp_corp_tools_',
1776 'mcp_corp_tools_',
1777 ])
1778 })
1779
1780 test("a name unlike a seated plugin's servers is nobody else's", () => {
1781 expect([
1782 Hooks.namesOrgTool(Fixtures.SEATED_SUITE, 'mine', 'greet'),
1783 Hooks.namesOrgTool(Fixtures.SEATED_SUITE, 'plugin_mine_jira', 'x'),
1784 Hooks.namesOrgTool(Fixtures.SEATED_SUITE, 'plugin_corp-suites', 'x'),
1785 Hooks.isOrgTool(Fixtures.SEATED_SUITE, 'mcp__plugin_mine_jira__x'),
1786 Hooks.isOrgTool(Fixtures.SEATED_SUITE, 'mcp__plugin_corp-suites_j__x'),
1787 Hooks.isOrgTool(Fixtures.SEATED_SUITE, 'mcp__mine__greet'),
1788 ]).toEqual([false, false, false, false, false, false])
1789 })
1790
1791 test('a server the allowlist names counts as one it delivers does', () => {
1792 expect(
1793 [Fixtures.ALLOWLIST, Fixtures.NO_ALLOWLIST, {}].map(policy =>
1794 Hooks.namesOrgTool(policy, 'corp__x', 'greet'),
1795 ),
1796 ).toEqual([true, false, false])
1797 })
1798
1799 test('a server named with a dot or a space owns its tools', () => {
1800 expect(
1801 ['mcp__corp_tools__x', 'mcp__my_wiki__x', 'mcp__corp__x'].map(tool =>
1802 Hooks.isOrgTool(
1803 { managedMcpServers: { 'corp.tools': {}, 'my wiki': {} } },
1804 tool,
1805 ),
1806 ),
1807 ).toEqual([true, true, false])
1808 })
1809
1810 test("an organization's ask cap holds over a plugin's allow", () => {
1811 expect(Hooks.holdsOver(Fixtures.CEILING_ASK, Fixtures.CEILING_ALLOW)).toBe(
1812 true,
1813 )
1814 })
1815
1816 test('the launcher variable is pinned where policy names a launcher', () => {
1817 expect(
1818 [
1819 'CLAUDE_CODE_PROCESS_WRAPPER',
1820 'claude_code_process_wrapper',
1821 'CORP_PROXY',
1822 ].map(name => Hooks.isPinnedVariable(Fixtures.WRAPPING_POLICY, name)),
1823 ).toEqual([true, true, false])
1824 })
1825
1826 test('a server whose name folds to nothing refuses every name', () => {
1827 expect(
1828 ['mine', 'corp', 'x__y'].map(plugin =>
1829 Hooks.namesOrgTool(Fixtures.UNFOLDABLE_POLICY, plugin, 'greet'),
1830 ),
1831 ).toEqual([true, true, true])
1832 })
1833
1834 test("a server's tools are told by its name whole, as the engine spells it", () => {
1835 expect(
1836 ['mcp__corp__x', 'mcp__corp-tools__x', 'mcp__Corp__x', 'mcp__--__x'].map(
1837 tool =>
1838 Hooks.isOrgTool({ managedMcpServers: { corp: {}, '--': {} } }, tool),
1839 ),
1840 ).toEqual([true, false, false, true])
1841 })
1842
1843 test(
1844 'a compaction is not theirs to answer',
1845 { plugins: [Fixtures.compacting] },
1846 async ($, on) => {
1847 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
1848 on('session.compact', ($, e) => ({ messages: e.messages }))
1849
1850 expect(
1851 await $.session.compact({
1852 trigger: 'plugin',
1853 messages: [Fixtures.COMPACTED_TURN],
1854 }),
1855 ).toEqual({ messages: [Fixtures.COMPACTED_TURN] })
1856 },
1857 )
1858
1859 test("a tool whose own name folds to nothing reads as the server's too", () => {
1860 expect(
1861 [
1862 ['Corp', '-'],
1863 ['Corp', '_'],
1864 ['other', '-'],
1865 ].map(([plugin = '', tool = '']) =>
1866 Hooks.namesOrgTool({ managedMcpServers: { corp: {} } }, plugin, tool),
1867 ),
1868 ).toEqual([true, true, false])
1869 })
1870
1871 test("an organization's own plugins' names are refused like its servers'", () => {
1872 const policy = {
1873 prependPlugins: ['corp-guard@market'],
1874 appendPlugins: ['corp-audit@market', 'plain'],
1875 }
1876
1877 expect(
1878 [
1879 ['Corp_Audit', 'x'],
1880 ['corp.guard', 'x'],
1881 ['PLAIN', 'x'],
1882 ['mine', 'x'],
1883 ].map(([plugin = '', tool = '']) =>
1884 Hooks.namesOrgTool(policy, plugin, tool),
1885 ),
1886 ).toEqual([true, true, true, false])
1887 expect(
1888 ['mcp__corp-audit__x', 'mcp__plain__x', 'mcp__mine__x'].map(tool =>
1889 Hooks.isOrgTool(policy, tool),
1890 ),
1891 ).toEqual([true, true, false])
1892 })
1893
1894 test('an unreadable server file refuses every name and owns every server tool', () => {
1895 const policy = { managedMcpServersUnreadable: true }
1896
1897 expect(Hooks.namesOrgTool(policy, 'mine', 'greet')).toBe(true)
1898 expect(
1899 ['mcp__any__x', 'Bash'].map(tool => Hooks.isOrgTool(policy, tool)),
1900 ).toEqual([true, false])
1901 expect(Hooks.isPinnedVariable(policy, 'CORP_PROXY')).toBe(false)
1902 })
1903
1904 test(
1905 "a compaction's summary is kept whole over the plugins they installed",
1906 { plugins: [Fixtures.blankingRows] },
1907 async ($, on) => {
1908 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
1909
1910 expect(await $.session.append(Fixtures.COMPACTION_ROW)).toMatchObject({
1911 message: { content: Fixtures.COMPACTION_ROW.message.content },
1912 })
1913 },
1914 )
1915
1916 test("a plugin managed settings turn on is the organization's by name too", () => {
1917 const policy = {
1918 enabledPlugins: {
1919 'corp-guard@corp-market': true,
1920 'left-off@corp-market': false,
1921 'diff@builtin': true,
1922 },
1923 }
1924
1925 expect(
1926 ['Corp_Guard', 'left-off', 'diff', 'mine'].map(plugin =>
1927 Hooks.namesOrgTool(policy, plugin, 'x'),
1928 ),
1929 ).toEqual([true, false, false, false])
1930 expect(
1931 ['mcp__corp-guard__x', 'mcp__left-off__x'].map(tool =>
1932 Hooks.isOrgTool(policy, tool),
1933 ),
1934 ).toEqual([true, false])
1935 })
1936
1937 test(
1938 "a skill the Skill tool hands over is kept whole; another tool's row is theirs",
1939 { plugins: [Fixtures.blankingRows] },
1940 async ($, on) => {
1941 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
1942
1943 expect(await $.session.append(Fixtures.SKILL_ROW)).toMatchObject({
1944 message: { content: Fixtures.SKILL_ROW.message.content },
1945 })
1946 expect(
1947 await $.session.append(Fixtures.READ_MESSAGE_ROW),
1948 ).not.toMatchObject({
1949 message: { content: Fixtures.READ_MESSAGE_ROW.message.content },
1950 })
1951 },
1952 )
1953
1954 test(
1955 "the organization's own plugin in the user tier keeps its name",
1956 {
1957 plugins: [
1958 Fixtures.registering('corp-guard'),
1959 Fixtures.registering('mine'),
1960 ],
1961 },
1962 async ($, on) => {
1963 on('settings.read', () => ({
1964 value: { enabledPlugins: { 'corp-guard@claude-plugin-test': true } },
1965 }))
1966
1967 const registered = Fixtures.toolsRegistered(on)
1968
1969 await $.session.start(Fixtures.SESSION)
1970
1971 expect(registered).toEqual(['corp-guard', 'mine'])
1972 },
1973 )
1974
1975 test(
1976 'and keeps it when a version list turns it on',
1977 {
1978 plugins: [
1979 Fixtures.registering('corp-guard'),
1980 Fixtures.registering('mine'),
1981 ],
1982 },
1983 async ($, on) => {
1984 on('settings.read', () => ({
1985 value: {
1986 enabledPlugins: { 'corp-guard@claude-plugin-test': ['^1.0.0'] },
1987 },
1988 }))
1989
1990 const registered = Fixtures.toolsRegistered(on)
1991
1992 await $.session.start(Fixtures.SESSION)
1993
1994 expect(registered).toEqual(['corp-guard', 'mine'])
1995 },
1996 )
1997
1998 test(
1999 'an id in another letter case is the same id, as the engine folds one',
2000 {
2001 plugins: [
2002 Fixtures.registering('corp-guard'),
2003 Fixtures.registering('mine'),
2004 ],
2005 },
2006 async ($, on) => {
2007 on('settings.read', () => ({
2008 value: { enabledPlugins: { 'Corp-Guard@Claude-Plugin-Test': true } },
2009 }))
2010
2011 const registered = Fixtures.toolsRegistered(on)
2012
2013 await $.session.start(Fixtures.SESSION)
2014
2015 expect(registered).toEqual(['corp-guard', 'mine'])
2016 },
2017 )
2018
2019 test(
2020 'an id listed only for its place exempts nobody',
2021 {
2022 plugins: [
2023 Fixtures.registering('corp-guard'),
2024 Fixtures.registering('mine'),
2025 ],
2026 },
2027 async ($, on) => {
2028 on('settings.read', () => ({
2029 value: { prependPlugins: ['corp-guard@claude-plugin-test'] },
2030 }))
2031
2032 const registered = Fixtures.toolsRegistered(on)
2033
2034 await $.session.start(Fixtures.SESSION)
2035
2036 expect(registered).toEqual(['mine'])
2037 },
2038 )
2039
2040 test(
2041 'an id managed settings turn off exempts nobody',
2042 {
2043 plugins: [
2044 Fixtures.registering('corp-guard'),
2045 Fixtures.registering('mine'),
2046 ],
2047 },
2048 async ($, on) => {
2049 on('settings.read', () => ({
2050 value: {
2051 enabledPlugins: { 'corp-guard@claude-plugin-test': false },
2052 appendPlugins: ['corp-guard@claude-plugin-test'],
2053 },
2054 }))
2055
2056 const registered = Fixtures.toolsRegistered(on)
2057
2058 await $.session.start(Fixtures.SESSION)
2059
2060 expect(registered).toEqual(['mine'])
2061 },
2062 )
2063
2064 test(
2065 "a plugin whose name only folds to the organization's is refused",
2066 {
2067 plugins: [
2068 Fixtures.registering('corp--guard'),
2069 Fixtures.registering('mine'),
2070 ],
2071 },
2072 async ($, on) => {
2073 on('settings.read', () => ({
2074 value: { enabledPlugins: { 'corp-guard@claude-plugin-test': true } },
2075 }))
2076
2077 const registered = Fixtures.toolsRegistered(on)
2078
2079 await $.session.start(Fixtures.SESSION)
2080
2081 expect(registered).toEqual(['mine'])
2082 },
2083 )
2084
2085 test(
2086 'the plugin the engine seats as the organization keeps its own name',
2087 { plugins: [Fixtures.registering('corp-guard', 'prepend')] },
2088 async ($, on) => {
2089 on('settings.read', () => ({
2090 value: { enabledPlugins: { 'corp-guard@claude-plugin-test': true } },
2091 }))
2092
2093 const registered = Fixtures.toolsRegistered(on)
2094
2095 await $.session.start(Fixtures.SESSION)
2096
2097 expect(registered).toEqual(['corp-guard'])
2098 },
2099 )
2100
2101 test(
2102 'a plugin of the same name from another source is refused it',
2103 {
2104 plugins: [
2105 Fixtures.registering('corp-guard'),
2106 Fixtures.registering('mine'),
2107 ],
2108 },
2109 async ($, on) => {
2110 on('settings.read', () => ({
2111 value: { enabledPlugins: { 'corp-guard@corp-market': true } },
2112 }))
2113
2114 const registered = Fixtures.toolsRegistered(on)
2115
2116 await $.session.start(Fixtures.SESSION)
2117
2118 expect(registered).toEqual(['mine'])
2119 },
2120 )
2121
2122 test(
2123 'the folder managed settings are read from is not theirs to repoint',
2124 { plugins: [Fixtures.repointingSettings('mine')] },
2125 async ($, on) => {
2126 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
2127 Fixtures.logged(on)
2128
2129 const set = Fixtures.variablesSet(on)
2130
2131 await $.session.start(Fixtures.SESSION)
2132
2133 expect(set).toEqual([])
2134 },
2135 )
2136
2137 test('that variable is pinned whatever managed settings hold, in any case', () => {
2138 expect(
2139 [
2140 'CLAUDE_CODE_MANAGED_SETTINGS_PATH',
2141 'claude_code_managed_settings_path',
2142 'CORP_PROXY',
2143 ].map(name => Hooks.isPinnedVariable({}, name)),
2144 ).toEqual([true, true, false])
2145 })
2146
2147 test('a scoped id is parted at its last at-sign', () => {
2148 const policy = { prependPlugins: ['@scope/corp-guard@npm'] }
2149
2150 expect(
2151 ['scope-corp-guard', 'Scope.Corp.Guard', 'mine'].map(plugin =>
2152 Hooks.namesOrgTool(policy, plugin, 'x'),
2153 ),
2154 ).toEqual([true, true, false])
2155 expect(Hooks.isOrgTool(policy, 'mcp__scope_corp-guard__x')).toBe(true)
2156 })
2157
2158 test('the override switch in a policy the person can write lifts nothing', () => {
2159 const written = {
2160 pluginConfigs: {
2161 'cc-plugin-sec-default@builtin': {
2162 options: { allowModsToOverrideDenyRules: true },
2163 },
2164 },
2165 }
2166
2167 expect(
2168 [written, { ...written, personWritablePolicy: true }].map(policy =>
2169 Hooks.denyRulesHold(policy),
2170 ),
2171 ).toEqual([false, true])
2172 })
2173
2174 test('every variable that says where policy is read from is pinned', () => {
2175 expect(
2176 [
2177 'CLAUDE_CODE_MANAGED_SETTINGS_PATH',
2178 'CLAUDE_CODE_REMOTE_SETTINGS_PATH',
2179 'CLAUDE_CODE_DISABLE_ADMIN_ENV_UNION',
2180 'CLAUDE_CODE_MOCK_REMOTE_SETTINGS',
2181 'CLAUDE_CONFIG_DIR',
2182 'CLAUDE_SECURESTORAGE_CONFIG_DIR',
2183 'USE_LOCAL_OAUTH',
2184 'USE_STAGING_OAUTH',
2185 'CLAUDE_LOCAL_OAUTH_API_BASE',
2186 ].map(name => Hooks.isPinnedVariable({}, name)),
2187 ).toEqual([true, true, true, true, true, true, true, true, true])
2188 })
2189
2190 test(
2191 'a listed id that is one at-sign refuses every registration of theirs',
2192 {
2193 plugins: [Fixtures.registering('mine'), Fixtures.registering('other')],
2194 },
2195 async ($, on) => {
2196 on('settings.read', () => ({ value: { prependPlugins: ['@'] } }))
2197
2198 const registered = Fixtures.toolsRegistered(on)
2199
2200 await $.session.start(Fixtures.SESSION)
2201
2202 expect(registered).toEqual([])
2203 },
2204 )
2205
2206 test('a listed id with no name half protects what follows its at-sign', () => {
2207 const policy = { prependPlugins: ['@market'] }
2208
2209 expect(
2210 ['market', 'Market', 'mine'].map(plugin =>
2211 Hooks.namesOrgTool(policy, plugin, 'x'),
2212 ),
2213 ).toEqual([true, true, false])
2214 })
2215})