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/ 5a2676e · 4 days ago History
2,437 lines · 68.3 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 "the read block's ask on a shell read 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.SETTING_ASK)
822
823 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.SETTING_ASK)
824
825 expect(lines).toEqual([
826 `transcript: ${Hooks.heldNotice('easy', 'Bash', Fixtures.SETTING_ASK, 'lift')}`,
827 ])
828 },
829 )
830
831 test(
832 "the mode's own ask, where a host answers it, holds over their allow",
833 { plugins: [Fixtures.allowing('easy')] },
834 async ($, on) => {
835 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
836
837 const lines = Fixtures.logged(on)
838
839 Fixtures.checksAnswered(on, Fixtures.HOST_ASK)
840
841 expect(await $.tool.check(Fixtures.CHECKED_BY_HOST)).toEqual(
842 Fixtures.HOST_ASK,
843 )
844
845 expect(lines).toEqual([
846 `transcript: ${Hooks.heldNotice('easy', 'Bash', Fixtures.HOST_ASK, 'lift')}`,
847 ])
848 },
849 )
850
851 test(
852 "an organization plugin's allow over a host's ask stands",
853 { plugins: [Fixtures.allowing('corp', 'append')] },
854 async ($, on) => {
855 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
856 Fixtures.checksAnswered(on, Fixtures.HOST_ASK)
857
858 expect(await $.tool.check(Fixtures.CHECKED_BY_HOST)).toEqual({
859 decision: 'allow',
860 })
861 },
862 )
863
864 test(
865 "a classic hook's deny holds over their allow",
866 { plugins: [Fixtures.allowing('easy')] },
867 async ($, on) => {
868 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
869
870 const lines = Fixtures.logged(on)
871
872 Fixtures.checksAnswered(on, Fixtures.HOOK_DENY)
873
874 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.HOOK_DENY)
875
876 expect(lines).toEqual([
877 `transcript: ${Hooks.heldNotice('easy', 'Bash', Fixtures.HOOK_DENY, 'lift')}`,
878 ])
879 },
880 )
881
882 test(
883 "a classic hook's deny holds over their ask",
884 { plugins: [Fixtures.asking('easy')] },
885 async ($, on) => {
886 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
887 Fixtures.logged(on)
888 Fixtures.checksAnswered(on, Fixtures.HOOK_DENY)
889
890 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.HOOK_DENY)
891 },
892 )
893
894 test(
895 'an allow that never called next meets the ask rule all the same',
896 { plugins: [Fixtures.blindAllowing] },
897 async ($, on) => {
898 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
899 Fixtures.logged(on)
900
901 const evaluations = Fixtures.checksAnswered(on, Fixtures.RULE_ASK)
902
903 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_ASK)
904 expect(evaluations()).toBe(1)
905 },
906 )
907
908 test(
909 "an organization plugin's allow over an ask rule stands",
910 {
911 plugins: [
912 Fixtures.allowing('suite', 'append'),
913 Fixtures.allowing('easy'),
914 ],
915 },
916 async ($, on) => {
917 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
918
919 const lines = Fixtures.logged(on)
920
921 Fixtures.checksAnswered(on, Fixtures.RULE_ASK)
922
923 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ALLOWED)
924 expect(lines).toEqual([])
925 },
926 )
927
928 test(
929 "an ask of theirs over an ask rule is the rule's ask again, whole",
930 { plugins: [Fixtures.asking('easy')] },
931 async ($, on) => {
932 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
933
934 const lines = Fixtures.logged(on)
935
936 Fixtures.checksAnswered(on, Fixtures.RULE_ASK)
937
938 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_ASK)
939
940 expect(lines).toEqual([
941 'transcript: ' +
942 Hooks.heldNotice('easy', 'Bash', Fixtures.RULE_ASK, 'reword'),
943 ])
944 },
945 )
946
947 test(
948 "a deny of theirs over a deny rule is the rule's deny again, whole",
949 { plugins: [Fixtures.tightening] },
950 async ($, on) => {
951 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
952
953 const lines = Fixtures.logged(on)
954
955 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
956
957 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
958
959 expect(lines).toEqual([
960 'transcript: ' +
961 Hooks.heldNotice('strict', 'Bash', Fixtures.RULE_DENY, 'reword'),
962 ])
963 },
964 )
965
966 test(
967 'an ask of theirs that says what it heard costs no second look',
968 { plugins: [Fixtures.asking('easy')] },
969 async ($, on) => {
970 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
971
972 const lines = Fixtures.logged(on)
973 const evaluations = Fixtures.checksAnswered(on, Fixtures.ASKED)
974
975 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ASKED)
976 expect({ lines, evaluations: evaluations() }).toEqual({
977 lines: [],
978 evaluations: 1,
979 })
980 },
981 )
982
983 test(
984 'the option that lets them override deny rules lets them override these',
985 { plugins: [Fixtures.allowing('easy')] },
986 async ($, on) => {
987 on('settings.read', () => ({ value: Fixtures.overridePolicyOf(true) }))
988
989 const lines = Fixtures.logged(on)
990
991 Fixtures.checksAnswered(on, Fixtures.RULE_ASK)
992
993 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ALLOWED)
994 expect(lines).toEqual([])
995 },
996 )
997
998 test(
999 "the mode's own ask, no rule or hook behind it, is theirs to allow",
1000 { plugins: [Fixtures.allowing('easy')] },
1001 async ($, on) => {
1002 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1003
1004 const lines = Fixtures.logged(on)
1005
1006 Fixtures.checksAnswered(on, Fixtures.ASKED)
1007
1008 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ALLOWED)
1009 expect(lines).toEqual([])
1010 },
1011 )
1012
1013 test(
1014 'a deny holds whatever decided it: it may stand before a rule or a hook',
1015 { plugins: [Fixtures.allowing('easy')] },
1016 async ($, on) => {
1017 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1018
1019 const lines = Fixtures.logged(on)
1020
1021 Fixtures.checksAnswered(on, Fixtures.PLAIN_DENY)
1022
1023 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.PLAIN_DENY)
1024
1025 expect(lines).toEqual([
1026 `transcript: ${Hooks.heldNotice('easy', 'Bash', Fixtures.PLAIN_DENY, 'lift')}`,
1027 ])
1028 },
1029 )
1030
1031 test(
1032 'the notice is no door: a plugin of theirs never runs inside this hook',
1033 { plugins: [Fixtures.sneaking] },
1034 async ($, on) => {
1035 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1036
1037 const lines = Fixtures.logged(on)
1038
1039 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
1040
1041 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
1042
1043 expect(lines).toEqual([
1044 'transcript: ' +
1045 Hooks.heldNotice('sneaking', 'Bash', Fixtures.RULE_DENY, 'lift'),
1046 ])
1047 },
1048 )
1049
1050 test(
1051 'nor is the notice theirs to drop',
1052 { plugins: [Fixtures.muting] },
1053 async ($, on) => {
1054 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1055
1056 const lines = Fixtures.logged(on)
1057
1058 Fixtures.checksAnswered(on, Fixtures.RULE_ASK)
1059
1060 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_ASK)
1061
1062 expect(lines).toEqual([
1063 `transcript: ${Hooks.heldNotice('muting', 'Bash', Fixtures.RULE_ASK, 'lift')}`,
1064 ])
1065 },
1066 )
1067
1068 test(
1069 "an organization's line is not theirs to drop; a line of their own is",
1070 {
1071 plugins: [
1072 Fixtures.muting,
1073 Fixtures.logging('mine'),
1074 Fixtures.logging('bundled', 'builtin'),
1075 Fixtures.logging('suite', 'append'),
1076 ],
1077 },
1078 async ($, on) => {
1079 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1080 on('session.start', ($, e) => ({ cwd: e.cwd }))
1081
1082 const heard: string[] = []
1083
1084 on('ui.log', ($, e, next) => {
1085 heard.push(next.origin.plugin)
1086
1087 return { value: undefined }
1088 })
1089
1090 await $.session.start(Fixtures.SESSION)
1091
1092 expect(heard).toEqual(['suite'])
1093 },
1094 )
1095
1096 test(
1097 'each plugin is told of a kind, by the tool it answered on',
1098 { plugins: [Fixtures.allowingBash, Fixtures.allowingRead] },
1099 async ($, on) => {
1100 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1101
1102 const lines = Fixtures.logged(on)
1103
1104 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
1105
1106 await $.tool.check(Fixtures.CHECKED)
1107 await $.tool.check(Fixtures.CHECKED_READ)
1108
1109 expect(lines).toEqual([
1110 `transcript: ${Hooks.heldNotice('basher', 'Bash', Fixtures.RULE_DENY, 'lift')}`,
1111 `transcript: ${Hooks.heldNotice('reader', 'Read', Fixtures.RULE_DENY, 'lift')}`,
1112 ])
1113 },
1114 )
1115
1116 test(
1117 "an organization's appended plugin has its say in the run past theirs",
1118 {
1119 plugins: [Fixtures.allowing('suite', 'append'), Fixtures.blindAllowing],
1120 },
1121 async ($, on) => {
1122 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1123
1124 const lines = Fixtures.logged(on)
1125
1126 Fixtures.checksAnswered(on, Fixtures.RULE_ASK)
1127
1128 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ALLOWED)
1129 expect(lines).toEqual([])
1130 },
1131 )
1132
1133 test(
1134 'a plugin is told once of each kind of thing that held over it',
1135 { plugins: [Fixtures.allowing('easy')] },
1136 async ($, on) => {
1137 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1138
1139 const lines = Fixtures.logged(on)
1140 const verdicts = [
1141 Fixtures.RULE_DENY,
1142 Fixtures.RULE_ASK,
1143 Fixtures.RULE_ASK,
1144 Fixtures.HOOK_ASK,
1145 Fixtures.PLAIN_DENY,
1146 Fixtures.RULE_DENY,
1147 ]
1148 const turn = Fixtures.checksAnsweredInTurn(on, verdicts)
1149
1150 for (const verdict of verdicts) {
1151 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(verdict)
1152 turn()
1153 }
1154
1155 expect(lines).toEqual(
1156 [
1157 Fixtures.RULE_DENY,
1158 Fixtures.RULE_ASK,
1159 Fixtures.HOOK_ASK,
1160 Fixtures.PLAIN_DENY,
1161 ].map(
1162 held =>
1163 `transcript: ${Hooks.heldNotice('easy', 'Bash', held, 'lift')}`,
1164 ),
1165 )
1166 },
1167 )
1168
1169 test(
1170 'a plugin of the person that tightens is heard, with one evaluation',
1171 { plugins: [Fixtures.tightening] },
1172 async ($, on) => {
1173 const reads = Fixtures.policyReads(on, Fixtures.MANAGED_POLICY)
1174 const evaluations = Fixtures.checksAnswered(on, Fixtures.ASKED)
1175
1176 await $.tool.check(Fixtures.CHECKED)
1177
1178 const loaded = { reads: reads(), evaluations: evaluations() }
1179
1180 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.PLAIN_DENY)
1181
1182 expect(
1183 {
1184 reads: reads() - loaded.reads,
1185 evaluations: evaluations() - loaded.evaluations,
1186 },
1187 'once its plugins have loaded, a check reads no policy',
1188 ).toEqual({ reads: 0, evaluations: 1 })
1189 },
1190 )
1191
1192 test(
1193 "a deny rule holds when an organization's plugin listens above theirs",
1194 {
1195 plugins: [
1196 Fixtures.listening('audit', 'prepend'),
1197 Fixtures.allowing('easy'),
1198 ],
1199 },
1200 async ($, on) => {
1201 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1202
1203 const lines = Fixtures.logged(on)
1204
1205 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
1206
1207 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
1208
1209 expect(
1210 lines.map(line => line.includes('easy')),
1211 'one line, naming the plugin of theirs, alone or in its batch',
1212 ).toEqual([true])
1213 },
1214 )
1215
1216 test(
1217 'a plugin of the person that only listens costs no second evaluation',
1218 { plugins: [Fixtures.listening('listening')] },
1219 async ($, on) => {
1220 const reads = Fixtures.policyReads(on, Fixtures.MANAGED_POLICY)
1221 const evaluations = Fixtures.checksAnswered(on, Fixtures.ASKED)
1222
1223 await $.tool.check(Fixtures.CHECKED)
1224
1225 const loaded = { reads: reads(), evaluations: evaluations() }
1226
1227 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ASKED)
1228
1229 expect(
1230 {
1231 reads: reads() - loaded.reads,
1232 evaluations: evaluations() - loaded.evaluations,
1233 },
1234 'once its plugins have loaded, a check reads no policy',
1235 ).toEqual({ reads: 0, evaluations: 1 })
1236 },
1237 )
1238
1239 test('with none of their plugins the verdict passes once', async ($, on) => {
1240 const reads = Fixtures.policyReads(on, Fixtures.MANAGED_POLICY)
1241 const evaluations = Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
1242
1243 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
1244
1245 expect({ reads: reads(), evaluations: evaluations() }).toEqual({
1246 reads: 0,
1247 evaluations: 1,
1248 })
1249 })
1250
1251 test(
1252 'an allow that never called next meets the deny rule all the same',
1253 { plugins: [Fixtures.blindAllowing] },
1254 async ($, on) => {
1255 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1256
1257 const lines = Fixtures.logged(on)
1258 const evaluations = Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
1259
1260 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
1261
1262 expect({ lines, evaluations: evaluations() }).toEqual({
1263 lines: [
1264 'transcript: ' +
1265 Hooks.heldNotice('blind', 'Bash', Fixtures.RULE_DENY, 'lift'),
1266 ],
1267 evaluations: 1,
1268 })
1269 },
1270 )
1271
1272 test(
1273 'a rule a plugin of the person writes into its answer is never read',
1274 { plugins: [Fixtures.forging] },
1275 async ($, on) => {
1276 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1277
1278 const lines = Fixtures.logged(on)
1279
1280 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
1281
1282 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
1283
1284 expect(lines).toEqual([
1285 'transcript: ' +
1286 Hooks.heldNotice('forging', 'Bash', Fixtures.RULE_DENY, 'lift'),
1287 ])
1288 },
1289 )
1290
1291 test(
1292 'a plugin of the person asking about another command is left out',
1293 { plugins: [Fixtures.rewriting] },
1294 async ($, on) => {
1295 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1296 Fixtures.logged(on)
1297
1298 const asked: unknown[] = []
1299
1300 on('tool.check', ($, e) => {
1301 asked.push(e.input)
1302
1303 return Fixtures.RULE_DENY
1304 })
1305
1306 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
1307
1308 expect(
1309 asked,
1310 'the rules were asked about the command that would run, only',
1311 ).toEqual([Fixtures.CHECKED.input])
1312 },
1313 )
1314
1315 test(
1316 'managed settings may let the plugins a person installs override',
1317 { plugins: [Fixtures.allowing('easy')] },
1318 async ($, on) => {
1319 on('settings.read', () => ({ value: Fixtures.overridePolicyOf(true) }))
1320
1321 const lines = Fixtures.logged(on)
1322 const evaluations = Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
1323
1324 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ALLOWED)
1325
1326 expect({ lines, evaluations: evaluations() }).toEqual({
1327 lines: [],
1328 evaluations: 1,
1329 })
1330 },
1331 )
1332
1333 test(
1334 "only managed settings are asked: a person's own cannot lift a deny rule",
1335 { plugins: [Fixtures.allowing('easy')] },
1336 async ($, on) => {
1337 Fixtures.policyBySource(
1338 on,
1339 Fixtures.MANAGED_POLICY,
1340 Fixtures.overridePolicyOf(true),
1341 )
1342
1343 Fixtures.logged(on)
1344 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
1345
1346 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
1347 },
1348 )
1349
1350 test(
1351 'the option mistyped lifts nothing: only the literal true does',
1352 { plugins: [Fixtures.allowing('easy')] },
1353 async ($, on) => {
1354 on('settings.read', () => ({ value: Fixtures.overridePolicyOf('true') }))
1355 Fixtures.logged(on)
1356 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
1357
1358 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
1359 },
1360 )
1361
1362 test(
1363 'with a policy that cannot be read the deny rule holds: fails closed',
1364 { plugins: [Fixtures.allowing('easy')] },
1365 async ($, on) => {
1366 const stopPolicy = Fixtures.policyUntilStopped(
1367 on,
1368 Fixtures.overridePolicyOf(true),
1369 'managed settings unreadable',
1370 )
1371
1372 Fixtures.logged(on)
1373 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
1374 on('prompt.section', ($, e) => ({ text: e.text }))
1375
1376 await $.prompt.section(Fixtures.MEMORY)
1377 stopPolicy()
1378
1379 expect(
1380 await $.tool.check(Fixtures.CHECKED),
1381 'the policy that read let plugins override; unread, the rule holds',
1382 ).toEqual(Fixtures.RULE_DENY)
1383 },
1384 )
1385
1386 test(
1387 "when the rules cannot be evaluated the call is refused: the hook's catch",
1388 { plugins: [Fixtures.blindAllowing] },
1389 async ($, on) => {
1390 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1391 Fixtures.logged(on)
1392
1393 on('tool.check', () => {
1394 throw new Error('the evaluation failed')
1395 })
1396
1397 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Hooks.UNCHECKED_DENY)
1398 },
1399 )
1400
1401 test('the refusal of a pinned variable names the setting and it', () => {
1402 expect(Hooks.pinnedVariableRefusal('CORP_PROXY')).toBe(
1403 'env (managed): CORP_PROXY is not for plugins outside policy to change',
1404 )
1405 })
1406
1407 test('a name managed env sets is pinned, in any case; no other is', () => {
1408 expect(
1409 ['CORP_PROXY', 'corp_proxy', 'Corp_Proxy', 'CORP', 'CORP_PROXY_2'].map(
1410 name => Hooks.isPinnedVariable(Fixtures.PINNING_POLICY, name),
1411 ),
1412 ).toEqual([true, true, true, false, false])
1413 })
1414
1415 test('whichever case managed env itself spells it in', () => {
1416 expect(
1417 ['CORP_PROXY', 'corp_proxy', 'CORP'].map(name =>
1418 Hooks.isPinnedVariable(Fixtures.LOWER_CASE_PINNING_POLICY, name),
1419 ),
1420 ).toEqual([true, true, false])
1421 })
1422
1423 test('a name managed env sets to nothing is pinned all the same', () => {
1424 expect(
1425 Hooks.isPinnedVariable({ env: { CORP_PROXY: '' } }, 'CORP_PROXY'),
1426 ).toBe(true)
1427 })
1428
1429 test('settings with no env pin nothing', () => {
1430 expect(
1431 [Fixtures.MANAGED_POLICY, {}, { env: {} }].map(policy =>
1432 Hooks.isPinnedVariable(policy, 'CORP_PROXY'),
1433 ),
1434 ).toEqual([false, false, false])
1435 })
1436
1437 test(
1438 'a variable managed settings pin is not for a plugin of the person to set',
1439 {
1440 plugins: [
1441 Fixtures.settingProxy('suite', 'prepend'),
1442 Fixtures.settingProxy('mine'),
1443 Fixtures.settingProxy('bundled', 'builtin'),
1444 ],
1445 },
1446 async ($, on) => {
1447 on('settings.read', () => ({ value: Fixtures.PINNING_POLICY }))
1448
1449 const lines = Fixtures.logged(on)
1450 const set = Fixtures.variablesSet(on)
1451
1452 await $.session.start(Fixtures.SESSION)
1453
1454 expect(set).toEqual(['suite CORP_PROXY=mine', 'bundled CORP_PROXY=mine'])
1455 expect(lines).toHaveLength(1)
1456 expect(lines[0]).toContain(Hooks.pinnedVariableRefusal('CORP_PROXY'))
1457 },
1458 )
1459
1460 test(
1461 'nor to unset',
1462 { plugins: [Fixtures.unsettingProxy] },
1463 async ($, on) => {
1464 on('settings.read', () => ({ value: Fixtures.PINNING_POLICY }))
1465
1466 const set = Fixtures.variablesSet(on)
1467
1468 await $.session.start(Fixtures.SESSION)
1469
1470 expect(set).toEqual([])
1471 },
1472 )
1473
1474 test(
1475 'nor under another case, which some systems read as the same name',
1476 { plugins: [Fixtures.settingLowerCase] },
1477 async ($, on) => {
1478 on('settings.read', () => ({ value: Fixtures.PINNING_POLICY }))
1479 Fixtures.logged(on)
1480
1481 const set = Fixtures.variablesSet(on)
1482
1483 await $.session.start(Fixtures.SESSION)
1484
1485 expect(set).toEqual([])
1486 },
1487 )
1488
1489 test(
1490 "what an organization's plugin reads is not theirs to answer",
1491 {
1492 plugins: [
1493 Fixtures.answeringReads,
1494 Fixtures.readingProxy('mine'),
1495 Fixtures.readingProxy('suite', 'append'),
1496 Fixtures.readingProxy('guard', 'prepend'),
1497 ],
1498 },
1499 async ($, on) => {
1500 on('settings.read', () => ({ value: Fixtures.PINNING_POLICY }))
1501 on('session.start', ($, e) => ({ cwd: e.cwd }))
1502 on('env.get', () => ({ value: 'as the engine holds it' }))
1503
1504 const lines = Fixtures.loggedBy(on)
1505
1506 await $.session.start(Fixtures.SESSION)
1507
1508 expect(lines.toSorted()).toEqual([
1509 'guard read as the engine holds it',
1510 'mine read theirs',
1511 'suite read as the engine holds it',
1512 ])
1513 },
1514 )
1515
1516 test(
1517 "nor to rewrite what an organization's plugin sets, pinned or not",
1518 {
1519 plugins: [
1520 Fixtures.revaluing,
1521 Fixtures.settingProxy('suite', 'append'),
1522 Fixtures.settingOther('audit', 'append'),
1523 Fixtures.settingOther('guard', 'prepend'),
1524 ],
1525 },
1526 async ($, on) => {
1527 on('settings.read', () => ({ value: Fixtures.PINNING_POLICY }))
1528
1529 const set = Fixtures.variablesSet(on)
1530
1531 await $.session.start(Fixtures.SESSION)
1532
1533 expect(set.toSorted()).toEqual([
1534 'audit OTHER=mine',
1535 'guard OTHER=mine',
1536 'suite CORP_PROXY=mine',
1537 ])
1538 },
1539 )
1540
1541 test(
1542 "an organization's write waits on no policy read, so none can fail it",
1543 {
1544 plugins: [Fixtures.revaluing, Fixtures.settingOther('audit', 'append')],
1545 },
1546 async ($, on) => {
1547 const stopPolicy = Fixtures.policyUntilStopped(
1548 on,
1549 Fixtures.MANAGED_POLICY,
1550 'managed settings unreadable',
1551 )
1552
1553 Fixtures.logged(on)
1554
1555 on('prompt.section', ($, e) => ({ text: e.text }))
1556
1557 const set = Fixtures.variablesSet(on)
1558
1559 await $.prompt.section(Fixtures.MEMORY)
1560 stopPolicy()
1561 await $.session.start(Fixtures.SESSION)
1562
1563 expect(set).toEqual(['audit OTHER=mine'])
1564 },
1565 )
1566
1567 test(
1568 "a built-in's write of a variable nobody pins still passes their hooks",
1569 {
1570 plugins: [
1571 Fixtures.revaluing,
1572 Fixtures.settingOther('bundled', 'builtin'),
1573 ],
1574 },
1575 async ($, on) => {
1576 on('settings.read', () => ({ value: Fixtures.PINNING_POLICY }))
1577
1578 const set = Fixtures.variablesSet(on)
1579
1580 await $.session.start(Fixtures.SESSION)
1581
1582 expect(set).toEqual(['bundled OTHER=theirs'])
1583 },
1584 )
1585
1586 test(
1587 'the option that lets them override deny rules unpins nothing',
1588 { plugins: [Fixtures.settingProxy('mine')] },
1589 async ($, on) => {
1590 on('settings.read', () => ({
1591 value: {
1592 ...Fixtures.overridePolicyOf(true),
1593 ...Fixtures.PINNING_POLICY,
1594 },
1595 }))
1596 Fixtures.logged(on)
1597
1598 const set = Fixtures.variablesSet(on)
1599
1600 await $.session.start(Fixtures.SESSION)
1601
1602 expect(set).toEqual([])
1603 },
1604 )
1605
1606 test(
1607 'a variable managed settings do not pin is theirs to set',
1608 { plugins: [Fixtures.settingOther('mine')] },
1609 async ($, on) => {
1610 on('settings.read', () => ({ value: Fixtures.PINNING_POLICY }))
1611
1612 const set = Fixtures.variablesSet(on)
1613
1614 await $.session.start(Fixtures.SESSION)
1615
1616 expect(set).toEqual(['mine OTHER=mine'])
1617 },
1618 )
1619
1620 test(
1621 "only managed settings pin: a person's own env binds no plugin",
1622 { plugins: [Fixtures.settingProxy('mine')] },
1623 async ($, on) => {
1624 Fixtures.policyBySource(
1625 on,
1626 Fixtures.MANAGED_POLICY,
1627 Fixtures.PINNING_POLICY,
1628 )
1629
1630 const set = Fixtures.variablesSet(on)
1631
1632 await $.session.start(Fixtures.SESSION)
1633
1634 expect(set).toEqual(['mine CORP_PROXY=mine'])
1635 },
1636 )
1637
1638 test(
1639 'with a policy that cannot be read they set no variable: fails closed',
1640 { plugins: [Fixtures.settingOther('mine')] },
1641 async ($, on) => {
1642 const stopPolicy = Fixtures.policyUntilStopped(
1643 on,
1644 Fixtures.MANAGED_POLICY,
1645 'managed settings unreadable',
1646 )
1647
1648 Fixtures.logged(on)
1649
1650 on('prompt.section', ($, e) => ({ text: e.text }))
1651
1652 const set = Fixtures.variablesSet(on)
1653
1654 await $.prompt.section(Fixtures.MEMORY)
1655 stopPolicy()
1656 await $.session.start(Fixtures.SESSION)
1657
1658 expect(set).toEqual([])
1659 },
1660 )
1661
1662 test("a server's own name, and any name under its tools' prefix", () => {
1663 expect(
1664 ['corp', 'corp__x', 'corp__x__y', 'corp._x'].map(plugin =>
1665 Hooks.namesOrgTool(Fixtures.SERVER_POLICY, plugin, 'greet'),
1666 ),
1667 ).toEqual([true, true, true, true])
1668 })
1669
1670 test('a name that only begins like one is no server of theirs', () => {
1671 expect(
1672 ['corporate', 'corpx', 'x__corp', 'x-corp', 'mine'].map(plugin =>
1673 Hooks.namesOrgTool(Fixtures.SERVER_POLICY, plugin, 'greet'),
1674 ),
1675 ).toEqual([false, false, false, false, false])
1676 })
1677
1678 test('another case or other separators spell the same server', () => {
1679 expect(
1680 ['Corp', 'CORP', 'corp_x', 'corp-x', 'corp.x', '_corp_'].map(plugin =>
1681 Hooks.namesOrgTool(Fixtures.SERVER_POLICY, plugin, 'greet'),
1682 ),
1683 ).toEqual([true, true, true, true, true, true])
1684 })
1685
1686 test('a claude.ai server with a run of spaces folds as its tools do', () => {
1687 expect(
1688 ['mcp__claude_ai_tools__x', 'mcp__claude_ai__tools__x'].map(tool =>
1689 Hooks.isOrgTool(
1690 { managedMcpServers: { 'claude.ai tools': {} } },
1691 tool,
1692 ),
1693 ),
1694 ).toEqual([true, true])
1695 })
1696
1697 test('a plugin and its tool that spell a server together count too', () => {
1698 expect(
1699 [
1700 ['corp', 'prod__deploy'],
1701 ['corp', 'deploy'],
1702 ['corp_tools', 'greet'],
1703 ['corp', 'tools__greet'],
1704 ].map(([plugin = '', tool = '']) =>
1705 Hooks.namesOrgTool(
1706 { managedMcpServers: { corp__prod: {}, 'corp.tools': {} } },
1707 plugin,
1708 tool,
1709 ),
1710 ),
1711 ).toEqual([true, false, true, true])
1712 })
1713
1714 test("a seated plugin's own servers are the organization's", () => {
1715 expect([
1716 Hooks.namesOrgTool(
1717 Fixtures.SEATED_SUITE,
1718 'plugin_corp-suite_corp-jira',
1719 'create_ticket',
1720 ),
1721 Hooks.namesOrgTool(Fixtures.SEATED_SUITE, 'Plugin.Corp_Suite.x', 'y'),
1722 Hooks.isOrgTool(
1723 Fixtures.SEATED_SUITE,
1724 'mcp__plugin_corp-suite_corp-jira__create_ticket',
1725 ),
1726 ]).toEqual([true, true, true])
1727 })
1728
1729 test('a seated plugin is told in any case, as the engine seats it', () => {
1730 const listed = { prependPlugins: ['Corp-Suite@market'] }
1731
1732 expect([
1733 Hooks.isOrgTool(listed, 'mcp__plugin_corp-suite_corp-jira__x'),
1734 Hooks.isOrgTool(listed, 'mcp__corp-suite__greet'),
1735 Hooks.isOrgTool(Fixtures.SEATED_SUITE, 'mcp__plugin_Corp-Suite_j__x'),
1736 Hooks.isOrgTool(listed, 'mcp__plugin_corp-suites_j__x'),
1737 Hooks.isOrgTool(Fixtures.SERVER_POLICY, 'mcp__Corp__search'),
1738 ]).toEqual([true, true, true, false, false])
1739 })
1740
1741 test('an entry that pins versions turns its plugin on, as true does', () => {
1742 const pinned = { enabledPlugins: { 'corp-suite@market': ['^1.0.0'] } }
1743 const off = { enabledPlugins: { 'corp-suite@market': false } }
1744
1745 expect([
1746 Hooks.namesOrgTool(pinned, 'corp-suite', 'greet'),
1747 Hooks.isOrgTool(pinned, 'mcp__corp-suite__greet'),
1748 Hooks.namesOrgTool(off, 'corp-suite', 'greet'),
1749 Hooks.namesOrgTool(pinned, 'mine', 'greet'),
1750 ]).toEqual([true, true, false, false])
1751 })
1752
1753 test('a name in the other normalization form is the same name', () => {
1754 const policy = { prependPlugins: ['caf\u00e9-suite@market'] }
1755
1756 expect([
1757 Hooks.namesOrgTool(policy, 'cafe\u0301-suite', 'x'),
1758 Hooks.isOrgTool(policy, 'mcp__plugin_cafe\u0301-suite_jira__x'),
1759 Hooks.isOrgTool(policy, 'mcp__cafe\u0301-suite__x'),
1760 ]).toEqual([true, true, true])
1761 })
1762
1763 test('only the id managed settings turn on keeps its own name', () => {
1764 const policy = {
1765 enabledPlugins: { 'corp-guard@corp-market': true },
1766 appendPlugins: ['corp-guard@corp-market'],
1767 }
1768 const asked = (id?: string) =>
1769 Hooks.namesOrgTool(policy, 'corp-guard', 'x', id)
1770
1771 expect([
1772 asked('corp-guard@corp-market'),
1773 asked('Corp-Guard@Corp-Market'),
1774 asked('corp-guard@inline'),
1775 asked('corp-guard@elsewhere'),
1776 asked('corp_guard@corp-market'),
1777 asked(),
1778 Hooks.namesOrgTool(
1779 { ...policy, managedMcpServersUnreadable: true },
1780 'corp-guard',
1781 'x',
1782 'corp-guard@corp-market',
1783 ),
1784 Hooks.namesOrgTool({}, 'corp-guard', 'x', 'corp-guard@inline'),
1785 ]).toEqual([false, false, true, true, true, true, true, false])
1786 })
1787
1788 test('an accented name is told as the engine spells it in a tool', () => {
1789 const composed = { prependPlugins: ['caf\u00e9-suite@market'] }
1790 const decomposed = { prependPlugins: ['cafe\u0301-suite@market'] }
1791
1792 expect([
1793 Hooks.isOrgTool(composed, 'mcp__caf_-suite__x'),
1794 Hooks.isOrgTool(composed, 'mcp__cafe_-suite__x'),
1795 Hooks.isOrgTool(decomposed, 'mcp__caf_-suite__x'),
1796 Hooks.isOrgTool(decomposed, 'mcp__cafe_-suite__x'),
1797 Hooks.isOrgTool(composed, 'mcp__plugin_caf_-suite_jira__x'),
1798 Hooks.namesOrgTool(composed, 'caf.-suite', 'x'),
1799 Hooks.namesOrgTool(composed, 'cafe\u0301-suite', 'x'),
1800 Hooks.namesOrgTool(decomposed, 'caf\u00e9-suite', 'x'),
1801 ]).toEqual([true, true, true, true, true, true, true, true])
1802 })
1803
1804 test('a combining mark on a plain letter does not part a name', () => {
1805 const decomposed = { prependPlugins: ['cafe\u0301-suite@market'] }
1806
1807 expect([
1808 Hooks.namesOrgTool(Fixtures.SEATED_SUITE, 'corp-suite\u0301', 'x'),
1809 Hooks.isOrgTool(
1810 Fixtures.SEATED_SUITE,
1811 'mcp__plugin_corp-suite\u0301_jira__x',
1812 ),
1813 Hooks.namesOrgTool(decomposed, 'cafe\u0301-suite', 'x'),
1814 Hooks.isOrgTool(decomposed, 'mcp__cafe\u0301-suite__x'),
1815 ]).toEqual([true, true, true, true])
1816 })
1817
1818 test('a name in plain letters folds exactly as before', () => {
1819 expect(
1820 ['Corp__Tools', 'corp-tools', 'corp.tools', 'CORP TOOLS'].map(
1821 Hooks.toolPrefixOf,
1822 ),
1823 ).toEqual([
1824 'mcp_corp_tools_',
1825 'mcp_corp_tools_',
1826 'mcp_corp_tools_',
1827 'mcp_corp_tools_',
1828 ])
1829 })
1830
1831 test("a name unlike a seated plugin's servers is nobody else's", () => {
1832 expect([
1833 Hooks.namesOrgTool(Fixtures.SEATED_SUITE, 'mine', 'greet'),
1834 Hooks.namesOrgTool(Fixtures.SEATED_SUITE, 'plugin_mine_jira', 'x'),
1835 Hooks.namesOrgTool(Fixtures.SEATED_SUITE, 'plugin_corp-suites', 'x'),
1836 Hooks.isOrgTool(Fixtures.SEATED_SUITE, 'mcp__plugin_mine_jira__x'),
1837 Hooks.isOrgTool(Fixtures.SEATED_SUITE, 'mcp__plugin_corp-suites_j__x'),
1838 Hooks.isOrgTool(Fixtures.SEATED_SUITE, 'mcp__mine__greet'),
1839 ]).toEqual([false, false, false, false, false, false])
1840 })
1841
1842 test('a server the allowlist names counts as one it delivers does', () => {
1843 expect(
1844 [Fixtures.ALLOWLIST, Fixtures.NO_ALLOWLIST, {}].map(policy =>
1845 Hooks.namesOrgTool(policy, 'corp__x', 'greet'),
1846 ),
1847 ).toEqual([true, false, false])
1848 })
1849
1850 test('a server named with a dot or a space owns its tools', () => {
1851 expect(
1852 ['mcp__corp_tools__x', 'mcp__my_wiki__x', 'mcp__corp__x'].map(tool =>
1853 Hooks.isOrgTool(
1854 { managedMcpServers: { 'corp.tools': {}, 'my wiki': {} } },
1855 tool,
1856 ),
1857 ),
1858 ).toEqual([true, true, false])
1859 })
1860
1861 test("an organization's ask cap holds over a plugin's allow", () => {
1862 expect(
1863 Hooks.holdsOver(Fixtures.CEILING_ASK, Fixtures.CEILING_ALLOW, undefined),
1864 ).toBe(true)
1865 })
1866
1867 test('the launcher variable is pinned where policy names a launcher', () => {
1868 expect(
1869 [
1870 'CLAUDE_CODE_PROCESS_WRAPPER',
1871 'claude_code_process_wrapper',
1872 'CORP_PROXY',
1873 ].map(name => Hooks.isPinnedVariable(Fixtures.WRAPPING_POLICY, name)),
1874 ).toEqual([true, true, false])
1875 })
1876
1877 test('a server whose name folds to nothing refuses every name', () => {
1878 expect(
1879 ['mine', 'corp', 'x__y'].map(plugin =>
1880 Hooks.namesOrgTool(Fixtures.UNFOLDABLE_POLICY, plugin, 'greet'),
1881 ),
1882 ).toEqual([true, true, true])
1883 })
1884
1885 test("a server's tools are told by its name whole, as the engine spells it", () => {
1886 expect(
1887 ['mcp__corp__x', 'mcp__corp-tools__x', 'mcp__Corp__x', 'mcp__--__x'].map(
1888 tool =>
1889 Hooks.isOrgTool({ managedMcpServers: { corp: {}, '--': {} } }, tool),
1890 ),
1891 ).toEqual([true, false, false, true])
1892 })
1893
1894 test("a server named corp does not claim a corp-tools server's tools", () => {
1895 expect(
1896 Hooks.isOrgTool(
1897 { managedMcpServers: { corp: {} } },
1898 'mcp__corp-tools__x',
1899 ),
1900 ).toBe(false)
1901 })
1902
1903 test(
1904 'a compaction is not theirs to answer',
1905 { plugins: [Fixtures.compacting] },
1906 async ($, on) => {
1907 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
1908 on('session.compact', ($, e) => ({ messages: e.messages }))
1909
1910 expect(
1911 await $.session.compact({
1912 trigger: 'plugin',
1913 messages: [Fixtures.COMPACTED_TURN],
1914 }),
1915 ).toEqual({ messages: [Fixtures.COMPACTED_TURN] })
1916 },
1917 )
1918
1919 test("a tool whose own name folds to nothing reads as the server's too", () => {
1920 expect(
1921 [
1922 ['Corp', '-'],
1923 ['Corp', '_'],
1924 ['other', '-'],
1925 ].map(([plugin = '', tool = '']) =>
1926 Hooks.namesOrgTool({ managedMcpServers: { corp: {} } }, plugin, tool),
1927 ),
1928 ).toEqual([true, true, false])
1929 })
1930
1931 test("an organization's own plugins' names are refused like its servers'", () => {
1932 const policy = {
1933 prependPlugins: ['corp-guard@market'],
1934 appendPlugins: ['corp-audit@market', 'plain'],
1935 }
1936
1937 expect(
1938 [
1939 ['Corp_Audit', 'x'],
1940 ['corp.guard', 'x'],
1941 ['PLAIN', 'x'],
1942 ['mine', 'x'],
1943 ].map(([plugin = '', tool = '']) =>
1944 Hooks.namesOrgTool(policy, plugin, tool),
1945 ),
1946 ).toEqual([true, true, true, false])
1947 expect(
1948 ['mcp__corp-audit__x', 'mcp__plain__x', 'mcp__mine__x'].map(tool =>
1949 Hooks.isOrgTool(policy, tool),
1950 ),
1951 ).toEqual([true, true, false])
1952 })
1953
1954 test('an unreadable server file refuses every name and owns every server tool', () => {
1955 const policy = { managedMcpServersUnreadable: true }
1956
1957 expect(Hooks.namesOrgTool(policy, 'mine', 'greet')).toBe(true)
1958 expect(
1959 ['mcp__any__x', 'Bash'].map(tool => Hooks.isOrgTool(policy, tool)),
1960 ).toEqual([true, false])
1961 expect(Hooks.isPinnedVariable(policy, 'CORP_PROXY')).toBe(false)
1962 })
1963
1964 test(
1965 "a compaction's summary is kept whole over the plugins they installed",
1966 { plugins: [Fixtures.blankingRows] },
1967 async ($, on) => {
1968 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
1969
1970 expect(await $.session.append(Fixtures.COMPACTION_ROW)).toMatchObject({
1971 message: { content: Fixtures.COMPACTION_ROW.message.content },
1972 })
1973 },
1974 )
1975
1976 test("a plugin managed settings turn on is the organization's by name too", () => {
1977 const policy = {
1978 enabledPlugins: {
1979 'corp-guard@corp-market': true,
1980 'left-off@corp-market': false,
1981 'diff@builtin': true,
1982 },
1983 }
1984
1985 expect(
1986 ['Corp_Guard', 'left-off', 'diff', 'mine'].map(plugin =>
1987 Hooks.namesOrgTool(policy, plugin, 'x'),
1988 ),
1989 ).toEqual([true, false, false, false])
1990 expect(
1991 ['mcp__corp-guard__x', 'mcp__left-off__x'].map(tool =>
1992 Hooks.isOrgTool(policy, tool),
1993 ),
1994 ).toEqual([true, false])
1995 })
1996
1997 test(
1998 "a skill the Skill tool hands over is kept whole; another tool's row is theirs",
1999 { plugins: [Fixtures.blankingRows] },
2000 async ($, on) => {
2001 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
2002
2003 expect(await $.session.append(Fixtures.SKILL_ROW)).toMatchObject({
2004 message: { content: Fixtures.SKILL_ROW.message.content },
2005 })
2006 expect(
2007 await $.session.append(Fixtures.READ_MESSAGE_ROW),
2008 ).not.toMatchObject({
2009 message: { content: Fixtures.READ_MESSAGE_ROW.message.content },
2010 })
2011 },
2012 )
2013
2014 test(
2015 "the organization's own plugin in the user tier keeps its name",
2016 {
2017 plugins: [
2018 Fixtures.registering('corp-guard'),
2019 Fixtures.registering('mine'),
2020 ],
2021 },
2022 async ($, on) => {
2023 on('settings.read', () => ({
2024 value: { enabledPlugins: { 'corp-guard@claude-plugin-test': true } },
2025 }))
2026
2027 const registered = Fixtures.toolsRegistered(on)
2028
2029 await $.session.start(Fixtures.SESSION)
2030
2031 expect(registered).toEqual(['corp-guard', 'mine'])
2032 },
2033 )
2034
2035 test(
2036 'and keeps it when a version list turns it on',
2037 {
2038 plugins: [
2039 Fixtures.registering('corp-guard'),
2040 Fixtures.registering('mine'),
2041 ],
2042 },
2043 async ($, on) => {
2044 on('settings.read', () => ({
2045 value: {
2046 enabledPlugins: { 'corp-guard@claude-plugin-test': ['^1.0.0'] },
2047 },
2048 }))
2049
2050 const registered = Fixtures.toolsRegistered(on)
2051
2052 await $.session.start(Fixtures.SESSION)
2053
2054 expect(registered).toEqual(['corp-guard', 'mine'])
2055 },
2056 )
2057
2058 test(
2059 'an id in another letter case is the same id, as the engine folds one',
2060 {
2061 plugins: [
2062 Fixtures.registering('corp-guard'),
2063 Fixtures.registering('mine'),
2064 ],
2065 },
2066 async ($, on) => {
2067 on('settings.read', () => ({
2068 value: { enabledPlugins: { 'Corp-Guard@Claude-Plugin-Test': true } },
2069 }))
2070
2071 const registered = Fixtures.toolsRegistered(on)
2072
2073 await $.session.start(Fixtures.SESSION)
2074
2075 expect(registered).toEqual(['corp-guard', 'mine'])
2076 },
2077 )
2078
2079 test(
2080 'an id listed only for its place exempts nobody',
2081 {
2082 plugins: [
2083 Fixtures.registering('corp-guard'),
2084 Fixtures.registering('mine'),
2085 ],
2086 },
2087 async ($, on) => {
2088 on('settings.read', () => ({
2089 value: { prependPlugins: ['corp-guard@claude-plugin-test'] },
2090 }))
2091
2092 const registered = Fixtures.toolsRegistered(on)
2093
2094 await $.session.start(Fixtures.SESSION)
2095
2096 expect(registered).toEqual(['mine'])
2097 },
2098 )
2099
2100 test(
2101 'an id managed settings turn off exempts nobody',
2102 {
2103 plugins: [
2104 Fixtures.registering('corp-guard'),
2105 Fixtures.registering('mine'),
2106 ],
2107 },
2108 async ($, on) => {
2109 on('settings.read', () => ({
2110 value: {
2111 enabledPlugins: { 'corp-guard@claude-plugin-test': false },
2112 appendPlugins: ['corp-guard@claude-plugin-test'],
2113 },
2114 }))
2115
2116 const registered = Fixtures.toolsRegistered(on)
2117
2118 await $.session.start(Fixtures.SESSION)
2119
2120 expect(registered).toEqual(['mine'])
2121 },
2122 )
2123
2124 test(
2125 "a plugin whose name only folds to the organization's is refused",
2126 {
2127 plugins: [
2128 Fixtures.registering('corp--guard'),
2129 Fixtures.registering('mine'),
2130 ],
2131 },
2132 async ($, on) => {
2133 on('settings.read', () => ({
2134 value: { enabledPlugins: { 'corp-guard@claude-plugin-test': true } },
2135 }))
2136
2137 const registered = Fixtures.toolsRegistered(on)
2138
2139 await $.session.start(Fixtures.SESSION)
2140
2141 expect(registered).toEqual(['mine'])
2142 },
2143 )
2144
2145 test(
2146 'the plugin the engine seats as the organization keeps its own name',
2147 { plugins: [Fixtures.registering('corp-guard', 'prepend')] },
2148 async ($, on) => {
2149 on('settings.read', () => ({
2150 value: { enabledPlugins: { 'corp-guard@claude-plugin-test': true } },
2151 }))
2152
2153 const registered = Fixtures.toolsRegistered(on)
2154
2155 await $.session.start(Fixtures.SESSION)
2156
2157 expect(registered).toEqual(['corp-guard'])
2158 },
2159 )
2160
2161 test(
2162 'a plugin of the same name from another source is refused it',
2163 {
2164 plugins: [
2165 Fixtures.registering('corp-guard'),
2166 Fixtures.registering('mine'),
2167 ],
2168 },
2169 async ($, on) => {
2170 on('settings.read', () => ({
2171 value: { enabledPlugins: { 'corp-guard@corp-market': true } },
2172 }))
2173
2174 const registered = Fixtures.toolsRegistered(on)
2175
2176 await $.session.start(Fixtures.SESSION)
2177
2178 expect(registered).toEqual(['mine'])
2179 },
2180 )
2181
2182 test(
2183 'the folder managed settings are read from is not theirs to repoint',
2184 { plugins: [Fixtures.repointingSettings('mine')] },
2185 async ($, on) => {
2186 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
2187 Fixtures.logged(on)
2188
2189 const set = Fixtures.variablesSet(on)
2190
2191 await $.session.start(Fixtures.SESSION)
2192
2193 expect(set).toEqual([])
2194 },
2195 )
2196
2197 test('that variable is pinned whatever managed settings hold, in any case', () => {
2198 expect(
2199 [
2200 'CLAUDE_CODE_MANAGED_SETTINGS_PATH',
2201 'claude_code_managed_settings_path',
2202 'CORP_PROXY',
2203 ].map(name => Hooks.isPinnedVariable({}, name)),
2204 ).toEqual([true, true, false])
2205 })
2206
2207 test('a scoped id is parted at its last at-sign', () => {
2208 const policy = { prependPlugins: ['@scope/corp-guard@npm'] }
2209
2210 expect(
2211 ['scope-corp-guard', 'Scope.Corp.Guard', 'mine'].map(plugin =>
2212 Hooks.namesOrgTool(policy, plugin, 'x'),
2213 ),
2214 ).toEqual([true, true, false])
2215 expect(Hooks.isOrgTool(policy, 'mcp__scope_corp-guard__x')).toBe(true)
2216 })
2217
2218 test('the override switch in a policy the person can write lifts nothing', () => {
2219 const written = {
2220 pluginConfigs: {
2221 'cc-plugin-sec-default@builtin': {
2222 options: { allowModsToOverrideDenyRules: true },
2223 },
2224 },
2225 }
2226
2227 expect(
2228 [written, { ...written, personWritablePolicy: true }].map(policy =>
2229 Hooks.denyRulesHold(policy),
2230 ),
2231 ).toEqual([false, true])
2232 })
2233
2234 test('every variable that says where policy is read from is pinned', () => {
2235 expect(
2236 [
2237 'CLAUDE_CODE_MANAGED_SETTINGS_PATH',
2238 'CLAUDE_CODE_REMOTE_SETTINGS_PATH',
2239 'CLAUDE_CODE_DISABLE_ADMIN_ENV_UNION',
2240 'CLAUDE_CODE_MOCK_REMOTE_SETTINGS',
2241 'CLAUDE_CONFIG_DIR',
2242 'CLAUDE_SECURESTORAGE_CONFIG_DIR',
2243 'USE_LOCAL_OAUTH',
2244 'USE_STAGING_OAUTH',
2245 'CLAUDE_LOCAL_OAUTH_API_BASE',
2246 ].map(name => Hooks.isPinnedVariable({}, name)),
2247 ).toEqual([true, true, true, true, true, true, true, true, true])
2248 })
2249
2250 test('every variable that can load code into a child is pinned', () => {
2251 expect(
2252 [
2253 'LD_PRELOAD',
2254 'LD_LIBRARY_PATH',
2255 'LD_AUDIT',
2256 'DYLD_INSERT_LIBRARIES',
2257 'DYLD_LIBRARY_PATH',
2258 'DYLD_FRAMEWORK_PATH',
2259 'BASH_ENV',
2260 'ENV',
2261 'ZDOTDIR',
2262 'NODE_OPTIONS',
2263 'ld_preload',
2264 'PATH',
2265 'HOME',
2266 ].map(name => Hooks.isPinnedVariable({}, name)),
2267 ).toEqual([
2268 true,
2269 true,
2270 true,
2271 true,
2272 true,
2273 true,
2274 true,
2275 true,
2276 true,
2277 true,
2278 true,
2279 false,
2280 false,
2281 ])
2282 })
2283
2284 test(
2285 "LD_PRELOAD is not a person's plugin's to set",
2286 {
2287 plugins: [
2288 Fixtures.settingLdPreload('suite', 'prepend'),
2289 Fixtures.settingLdPreload('mine'),
2290 Fixtures.settingLdPreload('bundled', 'builtin'),
2291 ],
2292 },
2293 async ($, on) => {
2294 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
2295
2296 const lines = Fixtures.logged(on)
2297 const set = Fixtures.variablesSet(on)
2298
2299 await $.session.start(Fixtures.SESSION)
2300
2301 expect(set).toEqual([
2302 'suite LD_PRELOAD=mine',
2303 'bundled LD_PRELOAD=mine',
2304 ])
2305 expect(lines).toHaveLength(1)
2306 expect(lines[0]).toContain(Hooks.pinnedVariableRefusal('LD_PRELOAD'))
2307 },
2308 )
2309
2310 test(
2311 "DYLD_INSERT_LIBRARIES is not a person's plugin's to set",
2312 {
2313 plugins: [
2314 Fixtures.settingDyldInsert('suite', 'prepend'),
2315 Fixtures.settingDyldInsert('mine'),
2316 Fixtures.settingDyldInsert('bundled', 'builtin'),
2317 ],
2318 },
2319 async ($, on) => {
2320 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
2321
2322 const lines = Fixtures.logged(on)
2323 const set = Fixtures.variablesSet(on)
2324
2325 await $.session.start(Fixtures.SESSION)
2326
2327 expect(set).toEqual([
2328 'suite DYLD_INSERT_LIBRARIES=mine',
2329 'bundled DYLD_INSERT_LIBRARIES=mine',
2330 ])
2331 expect(lines).toHaveLength(1)
2332 expect(lines[0]).toContain(
2333 Hooks.pinnedVariableRefusal('DYLD_INSERT_LIBRARIES'),
2334 )
2335 },
2336 )
2337
2338 test(
2339 "BASH_ENV is not a person's plugin's to set",
2340 {
2341 plugins: [
2342 Fixtures.settingBashEnv('suite', 'prepend'),
2343 Fixtures.settingBashEnv('mine'),
2344 Fixtures.settingBashEnv('bundled', 'builtin'),
2345 ],
2346 },
2347 async ($, on) => {
2348 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
2349
2350 const lines = Fixtures.logged(on)
2351 const set = Fixtures.variablesSet(on)
2352
2353 await $.session.start(Fixtures.SESSION)
2354
2355 expect(set).toEqual([
2356 'suite BASH_ENV=mine',
2357 'bundled BASH_ENV=mine',
2358 ])
2359 expect(lines).toHaveLength(1)
2360 expect(lines[0]).toContain(Hooks.pinnedVariableRefusal('BASH_ENV'))
2361 },
2362 )
2363
2364 test(
2365 "NODE_OPTIONS is not a person's plugin's to set",
2366 {
2367 plugins: [
2368 Fixtures.settingNodeOptions('suite', 'prepend'),
2369 Fixtures.settingNodeOptions('mine'),
2370 Fixtures.settingNodeOptions('bundled', 'builtin'),
2371 ],
2372 },
2373 async ($, on) => {
2374 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
2375
2376 const lines = Fixtures.logged(on)
2377 const set = Fixtures.variablesSet(on)
2378
2379 await $.session.start(Fixtures.SESSION)
2380
2381 expect(set).toEqual([
2382 'suite NODE_OPTIONS=mine',
2383 'bundled NODE_OPTIONS=mine',
2384 ])
2385 expect(lines).toHaveLength(1)
2386 expect(lines[0]).toContain(Hooks.pinnedVariableRefusal('NODE_OPTIONS'))
2387 },
2388 )
2389
2390 test(
2391 'nor is NODE_OPTIONS theirs to unset',
2392 {
2393 plugins: [
2394 Fixtures.unsettingNodeOptions('suite', 'prepend'),
2395 Fixtures.unsettingNodeOptions('mine'),
2396 ],
2397 },
2398 async ($, on) => {
2399 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
2400
2401 const lines = Fixtures.logged(on)
2402 const set = Fixtures.variablesSet(on)
2403
2404 await $.session.start(Fixtures.SESSION)
2405
2406 expect(set).toEqual(['suite NODE_OPTIONS=undefined'])
2407 expect(lines).toHaveLength(1)
2408 expect(lines[0]).toContain(Hooks.pinnedVariableRefusal('NODE_OPTIONS'))
2409 },
2410 )
2411
2412 test(
2413 'a listed id that is one at-sign refuses every registration of theirs',
2414 {
2415 plugins: [Fixtures.registering('mine'), Fixtures.registering('other')],
2416 },
2417 async ($, on) => {
2418 on('settings.read', () => ({ value: { prependPlugins: ['@'] } }))
2419
2420 const registered = Fixtures.toolsRegistered(on)
2421
2422 await $.session.start(Fixtures.SESSION)
2423
2424 expect(registered).toEqual([])
2425 },
2426 )
2427
2428 test('a listed id with no name half protects what follows its at-sign', () => {
2429 const policy = { prependPlugins: ['@market'] }
2430
2431 expect(
2432 ['market', 'Market', 'mine'].map(plugin =>
2433 Hooks.namesOrgTool(policy, plugin, 'x'),
2434 ),
2435 ).toEqual([true, true, false])
2436 })
2437})