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
1,229 lines · 33.8 KBRaw
1import { describe, expect, test, tier } from 'claude-code/testing'
2
3import Hooks from '../hooks'
4import Fixtures from './fixtures'
5
6tier('prepend')
7
8describe('register', () => {
9 test(
10 'under an MCP allowlist a plugin the person installed may not add a tool',
11 {
12 plugins: [
13 Fixtures.registering('suite', 'prepend'),
14 Fixtures.registering('mine'),
15 Fixtures.registering('bundled', 'builtin'),
16 ],
17 },
18 async ($, on) => {
19 on('settings.read', () => ({ value: Fixtures.ALLOWLIST }))
20
21 const registered = Fixtures.toolsRegistered(on)
22
23 await $.session.start(Fixtures.SESSION)
24
25 expect(registered).toEqual(['suite', 'bundled'])
26 },
27 )
28
29 test(
30 'with no allowlist, a plugin the person installed adds its tool',
31 { plugins: [Fixtures.registering('mine')] },
32 async ($, on) => {
33 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
34
35 const registered = Fixtures.toolsRegistered(on)
36
37 await $.session.start(Fixtures.SESSION)
38
39 expect(registered).toEqual(['mine'])
40 },
41 )
42
43 test(
44 "an organization's registration passes over a refusing user plugin",
45 {
46 plugins: [
47 Fixtures.registering('suite', 'prepend'),
48 Fixtures.denying,
49 Fixtures.registering('bundled', 'builtin'),
50 ],
51 },
52 async ($, on) => {
53 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
54
55 const registered = Fixtures.toolsRegistered(on)
56
57 await $.session.start(Fixtures.SESSION)
58
59 expect(
60 registered,
61 "a built-in's registration still meets the user's deny",
62 ).toEqual(['suite'])
63 },
64 )
65
66 test(
67 'a policy that cannot be read counts as one in force',
68 { plugins: [Fixtures.registering('mine')] },
69 async ($, on) => {
70 const stopPolicy = Fixtures.policyUntilStopped(
71 on,
72 Fixtures.NO_ALLOWLIST,
73 'managed settings unreadable',
74 )
75
76 const registered = Fixtures.toolsRegistered(on)
77
78 on('prompt.section', ($, e) => ({ text: e.text }))
79
80 await $.prompt.section(Fixtures.MEMORY)
81 stopPolicy()
82 await $.session.start(Fixtures.SESSION)
83
84 expect(registered).toEqual([])
85 },
86 )
87
88 test(
89 'the organization tools are listed as its tiers listed them',
90 { plugins: [Fixtures.relabeling, Fixtures.listing] },
91 async ($, on) => {
92 on('settings.read', () => ({ value: Fixtures.ALLOWLIST }))
93 on('tool.list', () => ({ value: [...Fixtures.TOOLS] }))
94
95 const { text } = await $.command.run(Fixtures.TOOLS_COMMAND)
96
97 expect(text?.split('\n')).toEqual([
98 'mcp__corp__search: Searches the corp wiki.',
99 'Bash: relabeled',
100 ])
101 },
102 )
103
104 test(
105 'a server policy delivers itself counts as a tool policy, unlisted',
106 { plugins: [Fixtures.relabeling, Fixtures.listing] },
107 async ($, on) => {
108 on('settings.read', () => ({ value: Fixtures.SERVER_POLICY }))
109 on('tool.list', () => ({ value: [...Fixtures.TOOLS] }))
110
111 const { text } = await $.command.run(Fixtures.TOOLS_COMMAND)
112
113 expect(text?.split('\n')).toEqual([
114 'mcp__corp__search: Searches the corp wiki.',
115 'Bash: relabeled',
116 ])
117 },
118 )
119
120 test(
121 "with no policy to read the organization's listing stands whole",
122 { plugins: [Fixtures.relabeling, Fixtures.listing] },
123 async ($, on) => {
124 const stopPolicy = Fixtures.policyUntilStopped(
125 on,
126 Fixtures.ALLOWLIST,
127 'settings unreadable',
128 )
129
130 on('prompt.section', ($, e) => ({ text: e.text }))
131 on('tool.list', () => ({ value: [...Fixtures.TOOLS] }))
132
133 await $.prompt.section(Fixtures.MEMORY)
134 stopPolicy()
135
136 const { text } = await $.command.run(Fixtures.TOOLS_COMMAND)
137
138 expect(text?.split('\n')).toEqual([
139 'mcp__corp__search: Searches the corp wiki.',
140 'Bash: Runs a command.',
141 ])
142 },
143 )
144
145 test(
146 'a prompt section passes over the plugins the person installed',
147 { plugins: [Fixtures.dropping, Fixtures.signing] },
148 async ($, on) => {
149 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
150 on('prompt.section', ($, e) => ({ text: e.text }))
151
152 expect(await $.prompt.section(Fixtures.MEMORY)).toEqual({
153 text: 'the org says hi (signed)',
154 })
155 },
156 )
157
158 test(
159 "the system prompt's sections pass over the plugins the person installed",
160 { plugins: [Fixtures.emptying, Fixtures.heading] },
161 async ($, on) => {
162 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
163
164 on('prompt.compose', () => ({
165 sections: [{ id: 'body', text: 'the body', scope: 'shared' }],
166 }))
167
168 expect(await $.prompt.compose(Fixtures.COMPOSED)).toEqual({
169 sections: [
170 { id: 'heading:org', text: 'the org says hi', scope: 'shared' },
171 { id: 'body', text: 'the body', scope: 'shared' },
172 ],
173 })
174 },
175 )
176
177 test(
178 "nor does a person's plugin drop or reword a section it asked for",
179 { plugins: [Fixtures.rewording, Fixtures.heading] },
180 async ($, on) => {
181 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
182
183 on('prompt.compose', () => ({
184 sections: [{ id: 'body', text: 'the body', scope: 'shared' }],
185 }))
186
187 expect(await $.prompt.compose(Fixtures.COMPOSED)).toEqual({
188 sections: [
189 { id: 'heading:org', text: 'the org says hi', scope: 'shared' },
190 { id: 'body', text: 'the body', scope: 'shared' },
191 ],
192 })
193 },
194 )
195
196 test(
197 "a user plugin's rewrite of policy is skipped for every other reader",
198 {
199 plugins: [
200 Fixtures.stripping,
201 Fixtures.reading,
202 Fixtures.registering('mine'),
203 ],
204 },
205 async ($, on) => {
206 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
207
208 const registered = Fixtures.toolsRegistered(on)
209 const { text } = await $.command.run(Fixtures.POLICY_COMMAND)
210
211 await $.session.start(Fixtures.SESSION)
212
213 expect(
214 JSON.parse(text ?? 'null'),
215 "another user plugin's read sees the allowlist the stripper hides",
216 ).toEqual(Fixtures.MANAGED_POLICY)
217
218 expect(
219 registered,
220 "sec-default's own tool.register hook still reads the allowlist",
221 ).toEqual([])
222 },
223 )
224
225 test(
226 "an organization provider's subject passes over the user plugins",
227 { plugins: [Fixtures.marking] },
228 async ($, on) => {
229 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
230 Fixtures.subjectsEchoed(on)
231
232 for (const provider of Fixtures.ORG_PROVIDERS) {
233 expect(
234 await $.tool.describe(
235 Fixtures.toolDescribed('mcp__corp__search', provider),
236 ),
237 ).toEqual({ description: 'd' })
238
239 expect(
240 (
241 await $.command.describe(
242 Fixtures.commandDescribed('suite:deploy', provider),
243 )
244 ).isHidden,
245 ).toBe(false)
246
247 expect(
248 await $.agent.offer(
249 Fixtures.agentOffered('suite:reviewer', provider),
250 ),
251 ).toEqual({ isOffered: true })
252
253 expect(await $.agent.spawn(Fixtures.agentSpawned(provider))).toEqual({
254 model: 'core',
255 })
256 }
257
258 for (const provider of Fixtures.USER_REACHABLE_PROVIDERS) {
259 expect(
260 await $.tool.describe(
261 Fixtures.toolDescribed('mcp__mine__search', provider),
262 ),
263 ).toEqual({ description: 'user: d' })
264
265 expect(
266 (
267 await $.command.describe(
268 Fixtures.commandDescribed('mine:deploy', provider),
269 )
270 ).isHidden,
271 ).toBe(true)
272
273 expect(
274 await $.agent.offer(Fixtures.agentOffered('reviewer', provider)),
275 ).toEqual({ isOffered: false })
276
277 expect(await $.agent.spawn(Fixtures.agentSpawned(provider))).toEqual({
278 model: 'user',
279 })
280 }
281 },
282 )
283
284 test(
285 'an odd provider passes over the user plugins: it fails closed',
286 { plugins: [Fixtures.marking] },
287 async ($, on) => {
288 on('settings.read', () => ({ value: Fixtures.NO_ALLOWLIST }))
289 Fixtures.subjectsEchoed(on)
290
291 for (const provider of Fixtures.ODD_PROVIDERS) {
292 expect(
293 await $.tool.describe(Fixtures.toolDescribed('Bash', provider)),
294 ).toEqual({ description: 'd' })
295
296 expect(await $.agent.spawn(Fixtures.agentSpawned(provider))).toEqual({
297 model: 'core',
298 })
299 }
300 },
301 )
302
303 test(
304 'a subject decision reads no policy; a burst of listings reads once',
305 { plugins: [Fixtures.listing] },
306 async ($, on) => {
307 const reads = Fixtures.policyReads(on, Fixtures.MANAGED_POLICY)
308
309 Fixtures.subjectsEchoed(on)
310 on('tool.list', () => ({ value: [...Fixtures.TOOLS] }))
311
312 for (const provider of Fixtures.ORG_PROVIDERS) {
313 await $.tool.describe(Fixtures.toolDescribed('mcp__corp__x', provider))
314 await $.agent.spawn(Fixtures.agentSpawned(provider))
315 }
316
317 expect(reads(), "one read: the person's plugin was admitted").toBe(1)
318
319 await Promise.all([
320 $.command.run(Fixtures.TOOLS_COMMAND),
321 $.command.run(Fixtures.TOOLS_COMMAND),
322 $.command.run(Fixtures.TOOLS_COMMAND),
323 ])
324
325 expect(reads()).toBe(2)
326 },
327 )
328
329 test(
330 'the refusal a user-tier caller reads names the allowlist',
331 {
332 plugins: [
333 {
334 name: 'asking',
335 register(on) {
336 on('command.run', { command: 'greet' }, $ =>
337 $.tool
338 .register({
339 name: 'greet',
340 description: 'Says hello.',
341 inputSchema: { type: 'object' },
342 })
343 .then(
344 () => ({ text: 'registered' }),
345 (error: unknown) => ({ text: String(error) }),
346 ),
347 )
348 },
349 },
350 ],
351 },
352 async ($, on) => {
353 on('settings.read', () => ({ value: Fixtures.ALLOWLIST }))
354
355 const { text } = await $.command.run({
356 command: 'greet',
357 args: '',
358 origin: { kind: 'composer' },
359 presentation: Fixtures.FULLSCREEN,
360 })
361
362 expect(text).toEndWith(
363 `asking: $.tool.register: ${Hooks.TOOL_REGISTER_REFUSAL}`,
364 )
365 },
366 )
367
368 test(
369 "under allowManagedModsOnly a person's mod is kept out, told why",
370 { plugins: [Fixtures.registering('mine')] },
371 async ($, on) => {
372 on('settings.read', () => ({ value: Fixtures.MANAGED_MODS_ONLY }))
373
374 const registered = Fixtures.toolsRegistered(on)
375
376 await expect($.session.start(Fixtures.SESSION)).rejects.toThrow(
377 Hooks.managedModsOnlyRefusal('mine'),
378 )
379
380 expect(registered, 'no hook of it ever ran').toEqual([])
381 },
382 )
383
384 test(
385 "under it the organization's mods and the built-ins load",
386 {
387 plugins: [
388 Fixtures.registering('suite', 'prepend'),
389 Fixtures.registering('after', 'append'),
390 Fixtures.registering('bundled', 'builtin'),
391 ],
392 },
393 async ($, on) => {
394 on('settings.read', () => ({ value: Fixtures.MANAGED_MODS_ONLY }))
395
396 const registered = Fixtures.toolsRegistered(on)
397
398 await $.session.start(Fixtures.SESSION)
399
400 expect(registered).toEqual(['suite', 'after', 'bundled'])
401 },
402 )
403
404 test(
405 'set to false, a mod the person installed loads as before',
406 { plugins: [Fixtures.registering('mine')] },
407 async ($, on) => {
408 on('settings.read', () => ({ value: Fixtures.modsPolicyOf(false) }))
409
410 const registered = Fixtures.toolsRegistered(on)
411
412 await $.session.start(Fixtures.SESSION)
413
414 expect(registered).toEqual(['mine'])
415 },
416 )
417
418 test(
419 "a person's copy under an organization mod's name is still theirs",
420 { plugins: [Fixtures.registering('suite')] },
421 async ($, on) => {
422 on('settings.read', () => ({
423 value: { ...Fixtures.MANAGED_POLICY, ...Fixtures.MANAGED_MODS_ONLY },
424 }))
425
426 const registered = Fixtures.toolsRegistered(on)
427
428 await expect($.session.start(Fixtures.SESSION)).rejects.toThrow(
429 Hooks.managedModsOnlyRefusal('suite'),
430 )
431
432 expect(registered).toEqual([])
433 },
434 )
435
436 test(
437 "only managed settings are asked: a person's own cannot loosen it",
438 { plugins: [Fixtures.registering('mine')] },
439 async ($, on) => {
440 Fixtures.policyBySource(
441 on,
442 Fixtures.MANAGED_MODS_ONLY,
443 Fixtures.modsPolicyOf(false),
444 )
445
446 const registered = Fixtures.toolsRegistered(on)
447
448 await expect($.session.start(Fixtures.SESSION)).rejects.toThrow(
449 Hooks.managedModsOnlyRefusal('mine'),
450 )
451
452 expect(registered).toEqual([])
453 },
454 )
455
456 test(
457 "nor turn it on: set only in a person's settings, it is not read",
458 { plugins: [Fixtures.registering('mine')] },
459 async ($, on) => {
460 Fixtures.policyBySource(
461 on,
462 Fixtures.NO_ALLOWLIST,
463 Fixtures.MANAGED_MODS_ONLY,
464 )
465
466 const registered = Fixtures.toolsRegistered(on)
467
468 await $.session.start(Fixtures.SESSION)
469
470 expect(registered).toEqual(['mine'])
471 },
472 )
473
474 test(
475 "a policy that cannot be read keeps a person's mod out: the hook's catch",
476 { plugins: [Fixtures.registering('mine')] },
477 async ($, on) => {
478 on('settings.read', () => ({ deny: 'managed settings unreadable' }))
479
480 const lines = Fixtures.logged(on)
481 const registered = Fixtures.toolsRegistered(on)
482
483 await expect($.session.start(Fixtures.SESSION)).rejects.toThrow(
484 Hooks.managedModsOnlyRefusal('mine'),
485 )
486
487 expect({ registered, lines }).toEqual({
488 registered: [],
489 lines: [
490 'debug: plugin.register hook failed judging mine (throw): ' +
491 'sec-default: $.settings.read: managed settings unreadable',
492 ],
493 })
494 },
495 )
496
497 test(
498 'a deny rule holds over an allow from a plugin the person installed',
499 { plugins: [Fixtures.allowing('easy')] },
500 async ($, on) => {
501 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
502
503 const lines = Fixtures.logged(on)
504
505 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
506
507 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
508
509 expect(lines).toEqual([
510 `transcript: ${Hooks.heldNotice('easy', 'Bash', Fixtures.RULE_DENY)}`,
511 ])
512 },
513 )
514
515 test(
516 'two plugins of the person chained are each named once in a session',
517 {
518 plugins: [Fixtures.allowing('first'), Fixtures.asking('second')],
519 },
520 async ($, on) => {
521 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
522
523 const lines = Fixtures.logged(on)
524
525 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
526
527 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
528 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
529
530 expect(lines.toSorted()).toEqual([
531 `transcript: ${Hooks.heldNotice('first', 'Bash', Fixtures.RULE_DENY)}`,
532 `transcript: ${Hooks.heldNotice('second', 'Bash', Fixtures.RULE_DENY)}`,
533 ])
534 },
535 )
536
537 test(
538 'a deny rule turned into an ask by a plugin of the person holds too',
539 { plugins: [Fixtures.asking('easy')] },
540 async ($, on) => {
541 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
542 Fixtures.logged(on)
543 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
544
545 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
546 },
547 )
548
549 test(
550 "an organization plugin's allow over a deny rule stands, above or below",
551 {
552 plugins: [
553 Fixtures.allowing('suite', 'append'),
554 Fixtures.allowing('easy'),
555 ],
556 },
557 async ($, on) => {
558 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
559
560 const lines = Fixtures.logged(on)
561
562 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
563
564 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ALLOWED)
565 expect(lines).toEqual([])
566 },
567 )
568
569 test(
570 "a prepended organization plugin's allow over a deny rule stands",
571 { plugins: [Fixtures.allowing('guard', 'prepend')] },
572 async ($, on) => {
573 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
574
575 const lines = Fixtures.logged(on)
576
577 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
578
579 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ALLOWED)
580 expect(lines).toEqual([])
581 },
582 )
583
584 test(
585 "a built-in's allow over a deny rule stands",
586 { plugins: [Fixtures.allowing('bundled', 'builtin')] },
587 async ($, on) => {
588 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
589
590 const lines = Fixtures.logged(on)
591
592 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
593
594 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ALLOWED)
595 expect(lines).toEqual([])
596 },
597 )
598
599 test(
600 'an ask rule holds over an allow from a plugin the person installed',
601 { plugins: [Fixtures.allowing('easy')] },
602 async ($, on) => {
603 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
604
605 const lines = Fixtures.logged(on)
606
607 Fixtures.checksAnswered(on, Fixtures.RULE_ASK)
608
609 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_ASK)
610
611 expect(lines).toEqual([
612 `transcript: ${Hooks.heldNotice('easy', 'Bash', Fixtures.RULE_ASK)}`,
613 ])
614 },
615 )
616
617 test(
618 'an allow that never called next meets the ask rule all the same',
619 { plugins: [Fixtures.blindAllowing] },
620 async ($, on) => {
621 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
622 Fixtures.logged(on)
623
624 const evaluations = Fixtures.checksAnswered(on, Fixtures.RULE_ASK)
625
626 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_ASK)
627 expect(evaluations()).toBe(1)
628 },
629 )
630
631 test(
632 "an organization plugin's allow over an ask rule stands",
633 {
634 plugins: [
635 Fixtures.allowing('suite', 'append'),
636 Fixtures.allowing('easy'),
637 ],
638 },
639 async ($, on) => {
640 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
641
642 const lines = Fixtures.logged(on)
643
644 Fixtures.checksAnswered(on, Fixtures.RULE_ASK)
645
646 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ALLOWED)
647 expect(lines).toEqual([])
648 },
649 )
650
651 test(
652 'an ask of theirs over an ask rule loosens nothing: one evaluation',
653 { plugins: [Fixtures.asking('easy')] },
654 async ($, on) => {
655 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
656
657 const lines = Fixtures.logged(on)
658 const evaluations = Fixtures.checksAnswered(on, Fixtures.RULE_ASK)
659
660 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ASKED)
661 expect({ lines, evaluations: evaluations() }).toEqual({
662 lines: [],
663 evaluations: 1,
664 })
665 },
666 )
667
668 test(
669 'the option that lets them override deny rules lets them override these',
670 { plugins: [Fixtures.allowing('easy')] },
671 async ($, on) => {
672 on('settings.read', () => ({ value: Fixtures.overridePolicyOf(true) }))
673
674 const lines = Fixtures.logged(on)
675
676 Fixtures.checksAnswered(on, Fixtures.RULE_ASK)
677
678 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ALLOWED)
679 expect(lines).toEqual([])
680 },
681 )
682
683 test(
684 "the mode's own ask, no rule behind it, is theirs to allow",
685 { plugins: [Fixtures.allowing('easy')] },
686 async ($, on) => {
687 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
688
689 const lines = Fixtures.logged(on)
690
691 Fixtures.checksAnswered(on, Fixtures.ASKED)
692
693 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ALLOWED)
694 expect(lines).toEqual([])
695 },
696 )
697
698 test(
699 "a deny holds whatever decided it: it may stand before a rule's ask",
700 { plugins: [Fixtures.allowing('easy')] },
701 async ($, on) => {
702 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
703
704 const lines = Fixtures.logged(on)
705
706 Fixtures.checksAnswered(on, Fixtures.PLAIN_DENY)
707
708 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.PLAIN_DENY)
709
710 expect(lines).toEqual([
711 `transcript: ${Hooks.heldNotice('easy', 'Bash', Fixtures.PLAIN_DENY)}`,
712 ])
713 },
714 )
715
716 test(
717 'the notice is no door: a plugin of theirs never runs inside this hook',
718 { plugins: [Fixtures.sneaking] },
719 async ($, on) => {
720 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
721
722 const lines = Fixtures.logged(on)
723
724 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
725
726 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
727
728 expect(lines).toEqual([
729 'transcript: ' +
730 Hooks.heldNotice('sneaking', 'Bash', Fixtures.RULE_DENY),
731 ])
732 },
733 )
734
735 test(
736 'nor is the notice theirs to drop',
737 { plugins: [Fixtures.muting] },
738 async ($, on) => {
739 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
740
741 const lines = Fixtures.logged(on)
742
743 Fixtures.checksAnswered(on, Fixtures.RULE_ASK)
744
745 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_ASK)
746
747 expect(lines).toEqual([
748 `transcript: ${Hooks.heldNotice('muting', 'Bash', Fixtures.RULE_ASK)}`,
749 ])
750 },
751 )
752
753 test(
754 "an organization's line is not theirs to drop; a line of their own is",
755 {
756 plugins: [
757 Fixtures.muting,
758 Fixtures.logging('mine'),
759 Fixtures.logging('bundled', 'builtin'),
760 Fixtures.logging('suite', 'append'),
761 ],
762 },
763 async ($, on) => {
764 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
765 on('session.start', ($, e) => ({ cwd: e.cwd }))
766
767 const heard: string[] = []
768
769 on('ui.log', ($, e, next) => {
770 heard.push(next.origin.plugin)
771
772 return { value: undefined }
773 })
774
775 await $.session.start(Fixtures.SESSION)
776
777 expect(heard).toEqual(['suite'])
778 },
779 )
780
781 test(
782 'each plugin is told of a kind, by the tool it answered on',
783 { plugins: [Fixtures.allowingBash, Fixtures.allowingRead] },
784 async ($, on) => {
785 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
786
787 const lines = Fixtures.logged(on)
788
789 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
790
791 await $.tool.check(Fixtures.CHECKED)
792 await $.tool.check(Fixtures.CHECKED_READ)
793
794 expect(lines).toEqual([
795 `transcript: ${Hooks.heldNotice('basher', 'Bash', Fixtures.RULE_DENY)}`,
796 `transcript: ${Hooks.heldNotice('reader', 'Read', Fixtures.RULE_DENY)}`,
797 ])
798 },
799 )
800
801 test(
802 "an organization's appended plugin has its say in the run past theirs",
803 {
804 plugins: [Fixtures.allowing('suite', 'append'), Fixtures.blindAllowing],
805 },
806 async ($, on) => {
807 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
808
809 const lines = Fixtures.logged(on)
810
811 Fixtures.checksAnswered(on, Fixtures.RULE_ASK)
812
813 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ALLOWED)
814 expect(lines).toEqual([])
815 },
816 )
817
818 test(
819 'a plugin is told once of each kind of thing that held over it',
820 { plugins: [Fixtures.allowing('easy')] },
821 async ($, on) => {
822 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
823
824 const lines = Fixtures.logged(on)
825 const verdicts = [
826 Fixtures.RULE_DENY,
827 Fixtures.RULE_ASK,
828 Fixtures.RULE_ASK,
829 Fixtures.PLAIN_DENY,
830 Fixtures.RULE_DENY,
831 ]
832 const turn = Fixtures.checksAnsweredInTurn(on, verdicts)
833
834 for (const verdict of verdicts) {
835 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(verdict)
836 turn()
837 }
838
839 expect(lines).toEqual(
840 [Fixtures.RULE_DENY, Fixtures.RULE_ASK, Fixtures.PLAIN_DENY].map(
841 held => `transcript: ${Hooks.heldNotice('easy', 'Bash', held)}`,
842 ),
843 )
844 },
845 )
846
847 test(
848 'a plugin of the person that tightens is heard, with one evaluation',
849 { plugins: [Fixtures.tightening] },
850 async ($, on) => {
851 const reads = Fixtures.policyReads(on, Fixtures.MANAGED_POLICY)
852 const evaluations = Fixtures.checksAnswered(on, Fixtures.ASKED)
853
854 await $.tool.check(Fixtures.CHECKED)
855
856 const loaded = { reads: reads(), evaluations: evaluations() }
857
858 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.PLAIN_DENY)
859
860 expect(
861 {
862 reads: reads() - loaded.reads,
863 evaluations: evaluations() - loaded.evaluations,
864 },
865 'once its plugins have loaded, a check reads no policy',
866 ).toEqual({ reads: 0, evaluations: 1 })
867 },
868 )
869
870 test(
871 "a deny rule holds when an organization's plugin listens above theirs",
872 {
873 plugins: [
874 Fixtures.listening('audit', 'prepend'),
875 Fixtures.allowing('easy'),
876 ],
877 },
878 async ($, on) => {
879 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
880
881 const lines = Fixtures.logged(on)
882
883 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
884
885 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
886
887 expect(
888 lines.map(line => line.includes('easy')),
889 'one line, naming the plugin of theirs, alone or in its batch',
890 ).toEqual([true])
891 },
892 )
893
894 test(
895 'a plugin of the person that only listens costs no second evaluation',
896 { plugins: [Fixtures.listening('listening')] },
897 async ($, on) => {
898 const reads = Fixtures.policyReads(on, Fixtures.MANAGED_POLICY)
899 const evaluations = Fixtures.checksAnswered(on, Fixtures.ASKED)
900
901 await $.tool.check(Fixtures.CHECKED)
902
903 const loaded = { reads: reads(), evaluations: evaluations() }
904
905 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ASKED)
906
907 expect(
908 {
909 reads: reads() - loaded.reads,
910 evaluations: evaluations() - loaded.evaluations,
911 },
912 'once its plugins have loaded, a check reads no policy',
913 ).toEqual({ reads: 0, evaluations: 1 })
914 },
915 )
916
917 test('with none of their plugins the verdict passes once', async ($, on) => {
918 const reads = Fixtures.policyReads(on, Fixtures.MANAGED_POLICY)
919 const evaluations = Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
920
921 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
922
923 expect({ reads: reads(), evaluations: evaluations() }).toEqual({
924 reads: 0,
925 evaluations: 1,
926 })
927 })
928
929 test(
930 'an allow that never called next meets the deny rule all the same',
931 { plugins: [Fixtures.blindAllowing] },
932 async ($, on) => {
933 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
934
935 const lines = Fixtures.logged(on)
936 const evaluations = Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
937
938 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
939
940 expect({ lines, evaluations: evaluations() }).toEqual({
941 lines: [
942 'transcript: ' +
943 Hooks.heldNotice('blind', 'Bash', Fixtures.RULE_DENY),
944 ],
945 evaluations: 1,
946 })
947 },
948 )
949
950 test(
951 'a rule a plugin of the person writes into its answer is never read',
952 { plugins: [Fixtures.forging] },
953 async ($, on) => {
954 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
955
956 const lines = Fixtures.logged(on)
957
958 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
959
960 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
961
962 expect(lines).toEqual([
963 'transcript: ' +
964 Hooks.heldNotice('forging', 'Bash', Fixtures.RULE_DENY),
965 ])
966 },
967 )
968
969 test(
970 'a plugin of the person asking about another command is left out',
971 { plugins: [Fixtures.rewriting] },
972 async ($, on) => {
973 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
974 Fixtures.logged(on)
975
976 const asked: unknown[] = []
977
978 on('tool.check', ($, e) => {
979 asked.push(e.input)
980
981 return Fixtures.RULE_DENY
982 })
983
984 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
985
986 expect(
987 asked,
988 'the rules were asked about the command that would run, only',
989 ).toEqual([Fixtures.CHECKED.input])
990 },
991 )
992
993 test(
994 'managed settings may let the plugins a person installs override',
995 { plugins: [Fixtures.allowing('easy')] },
996 async ($, on) => {
997 on('settings.read', () => ({ value: Fixtures.overridePolicyOf(true) }))
998
999 const lines = Fixtures.logged(on)
1000 const evaluations = Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
1001
1002 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ALLOWED)
1003
1004 expect({ lines, evaluations: evaluations() }).toEqual({
1005 lines: [],
1006 evaluations: 1,
1007 })
1008 },
1009 )
1010
1011 test(
1012 "only managed settings are asked: a person's own cannot lift a deny rule",
1013 { plugins: [Fixtures.allowing('easy')] },
1014 async ($, on) => {
1015 Fixtures.policyBySource(
1016 on,
1017 Fixtures.MANAGED_POLICY,
1018 Fixtures.overridePolicyOf(true),
1019 )
1020
1021 Fixtures.logged(on)
1022 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
1023
1024 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
1025 },
1026 )
1027
1028 test(
1029 'the option mistyped lifts nothing: only the literal true does',
1030 { plugins: [Fixtures.allowing('easy')] },
1031 async ($, on) => {
1032 on('settings.read', () => ({ value: Fixtures.overridePolicyOf('true') }))
1033 Fixtures.logged(on)
1034 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
1035
1036 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
1037 },
1038 )
1039
1040 test(
1041 'with a policy that cannot be read the deny rule holds: fails closed',
1042 { plugins: [Fixtures.allowing('easy')] },
1043 async ($, on) => {
1044 const stopPolicy = Fixtures.policyUntilStopped(
1045 on,
1046 Fixtures.overridePolicyOf(true),
1047 'managed settings unreadable',
1048 )
1049
1050 Fixtures.logged(on)
1051 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
1052 on('prompt.section', ($, e) => ({ text: e.text }))
1053
1054 await $.prompt.section(Fixtures.MEMORY)
1055 stopPolicy()
1056
1057 expect(
1058 await $.tool.check(Fixtures.CHECKED),
1059 'the policy that read let plugins override; unread, the rule holds',
1060 ).toEqual(Fixtures.RULE_DENY)
1061 },
1062 )
1063
1064 test(
1065 "when the rules cannot be evaluated the call is refused: the hook's catch",
1066 { plugins: [Fixtures.blindAllowing] },
1067 async ($, on) => {
1068 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1069 Fixtures.logged(on)
1070
1071 on('tool.check', () => {
1072 throw new Error('the evaluation failed')
1073 })
1074
1075 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Hooks.UNCHECKED_DENY)
1076 },
1077 )
1078
1079 test(
1080 'a variable managed settings pin is not for a plugin of the person to set',
1081 {
1082 plugins: [
1083 Fixtures.settingProxy('suite', 'prepend'),
1084 Fixtures.settingProxy('mine'),
1085 Fixtures.settingProxy('bundled', 'builtin'),
1086 ],
1087 },
1088 async ($, on) => {
1089 on('settings.read', () => ({ value: Fixtures.PINNING_POLICY }))
1090
1091 const lines = Fixtures.logged(on)
1092 const set = Fixtures.variablesSet(on)
1093
1094 await $.session.start(Fixtures.SESSION)
1095
1096 expect(set).toEqual(['suite CORP_PROXY=mine', 'bundled CORP_PROXY=mine'])
1097 expect(lines).toHaveLength(1)
1098 expect(lines[0]).toContain(Hooks.pinnedVariableRefusal('CORP_PROXY'))
1099 },
1100 )
1101
1102 test(
1103 'nor to unset',
1104 { plugins: [Fixtures.unsettingProxy] },
1105 async ($, on) => {
1106 on('settings.read', () => ({ value: Fixtures.PINNING_POLICY }))
1107
1108 const set = Fixtures.variablesSet(on)
1109
1110 await $.session.start(Fixtures.SESSION)
1111
1112 expect(set).toEqual([])
1113 },
1114 )
1115
1116 test(
1117 'nor under another case, which some systems read as the same name',
1118 { plugins: [Fixtures.settingLowerCase] },
1119 async ($, on) => {
1120 on('settings.read', () => ({ value: Fixtures.PINNING_POLICY }))
1121 Fixtures.logged(on)
1122
1123 const set = Fixtures.variablesSet(on)
1124
1125 await $.session.start(Fixtures.SESSION)
1126
1127 expect(set).toEqual([])
1128 },
1129 )
1130
1131 test(
1132 "nor to rewrite while an organization's plugin sets it",
1133 {
1134 plugins: [
1135 Fixtures.revaluing,
1136 Fixtures.settingProxy('suite', 'append'),
1137 Fixtures.settingOther('audit', 'append'),
1138 ],
1139 },
1140 async ($, on) => {
1141 on('settings.read', () => ({ value: Fixtures.PINNING_POLICY }))
1142
1143 const set = Fixtures.variablesSet(on)
1144
1145 await $.session.start(Fixtures.SESSION)
1146
1147 expect(set.toSorted()).toEqual([
1148 'audit OTHER=theirs',
1149 'suite CORP_PROXY=mine',
1150 ])
1151 },
1152 )
1153
1154 test(
1155 'the option that lets them override deny rules unpins nothing',
1156 { plugins: [Fixtures.settingProxy('mine')] },
1157 async ($, on) => {
1158 on('settings.read', () => ({
1159 value: {
1160 ...Fixtures.overridePolicyOf(true),
1161 ...Fixtures.PINNING_POLICY,
1162 },
1163 }))
1164 Fixtures.logged(on)
1165
1166 const set = Fixtures.variablesSet(on)
1167
1168 await $.session.start(Fixtures.SESSION)
1169
1170 expect(set).toEqual([])
1171 },
1172 )
1173
1174 test(
1175 'a variable managed settings do not pin is theirs to set',
1176 { plugins: [Fixtures.settingOther('mine')] },
1177 async ($, on) => {
1178 on('settings.read', () => ({ value: Fixtures.PINNING_POLICY }))
1179
1180 const set = Fixtures.variablesSet(on)
1181
1182 await $.session.start(Fixtures.SESSION)
1183
1184 expect(set).toEqual(['mine OTHER=mine'])
1185 },
1186 )
1187
1188 test(
1189 "only managed settings pin: a person's own env binds no plugin",
1190 { plugins: [Fixtures.settingProxy('mine')] },
1191 async ($, on) => {
1192 Fixtures.policyBySource(
1193 on,
1194 Fixtures.MANAGED_POLICY,
1195 Fixtures.PINNING_POLICY,
1196 )
1197
1198 const set = Fixtures.variablesSet(on)
1199
1200 await $.session.start(Fixtures.SESSION)
1201
1202 expect(set).toEqual(['mine CORP_PROXY=mine'])
1203 },
1204 )
1205
1206 test(
1207 'with a policy that cannot be read they set no variable: fails closed',
1208 { plugins: [Fixtures.settingOther('mine')] },
1209 async ($, on) => {
1210 const stopPolicy = Fixtures.policyUntilStopped(
1211 on,
1212 Fixtures.MANAGED_POLICY,
1213 'managed settings unreadable',
1214 )
1215
1216 Fixtures.logged(on)
1217
1218 on('prompt.section', ($, e) => ({ text: e.text }))
1219
1220 const set = Fixtures.variablesSet(on)
1221
1222 await $.prompt.section(Fixtures.MEMORY)
1223 stopPolicy()
1224 await $.session.start(Fixtures.SESSION)
1225
1226 expect(set).toEqual([])
1227 },
1228 )
1229})