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,204 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', 'Bash(echo *)')}`,
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', 'Bash(echo *)')}`,
532 `transcript: ${Hooks.heldNotice('second', 'Bash', 'Bash(echo *)')}`,
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 a plugin of the person allows, no deny rule behind it, stands',
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.ASKED)
608
609 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ALLOWED)
610 expect(lines).toEqual([])
611 },
612 )
613
614 test(
615 'a deny no rule decided is still theirs to answer over',
616 { plugins: [Fixtures.allowing('easy')] },
617 async ($, on) => {
618 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
619
620 const lines = Fixtures.logged(on)
621
622 Fixtures.checksAnswered(on, Fixtures.PLAIN_DENY)
623
624 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ALLOWED)
625 expect(lines).toEqual([])
626 },
627 )
628
629 test(
630 'a plugin of the person that tightens is heard, with one evaluation',
631 { plugins: [Fixtures.tightening] },
632 async ($, on) => {
633 const reads = Fixtures.policyReads(on, Fixtures.MANAGED_POLICY)
634 const evaluations = Fixtures.checksAnswered(on, Fixtures.ASKED)
635
636 await $.tool.check(Fixtures.CHECKED)
637
638 const loaded = { reads: reads(), evaluations: evaluations() }
639
640 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.PLAIN_DENY)
641
642 expect(
643 {
644 reads: reads() - loaded.reads,
645 evaluations: evaluations() - loaded.evaluations,
646 },
647 'once its plugins have loaded, a check reads no policy',
648 ).toEqual({ reads: 0, evaluations: 1 })
649 },
650 )
651
652 test(
653 "a deny rule holds when an organization's plugin listens above theirs",
654 {
655 plugins: [
656 Fixtures.listening('audit', 'prepend'),
657 Fixtures.allowing('easy'),
658 ],
659 },
660 async ($, on) => {
661 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
662
663 const lines = Fixtures.logged(on)
664
665 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
666
667 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
668
669 expect(
670 lines.map(line => line.includes('easy')),
671 'one line, naming the plugin of theirs, alone or in its batch',
672 ).toEqual([true])
673 },
674 )
675
676 test(
677 'a plugin of the person that only listens costs no second evaluation',
678 { plugins: [Fixtures.listening('listening')] },
679 async ($, on) => {
680 const reads = Fixtures.policyReads(on, Fixtures.MANAGED_POLICY)
681 const evaluations = Fixtures.checksAnswered(on, Fixtures.ASKED)
682
683 await $.tool.check(Fixtures.CHECKED)
684
685 const loaded = { reads: reads(), evaluations: evaluations() }
686
687 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ASKED)
688
689 expect(
690 {
691 reads: reads() - loaded.reads,
692 evaluations: evaluations() - loaded.evaluations,
693 },
694 'once its plugins have loaded, a check reads no policy',
695 ).toEqual({ reads: 0, evaluations: 1 })
696 },
697 )
698
699 test('with none of their plugins the verdict passes once', async ($, on) => {
700 const reads = Fixtures.policyReads(on, Fixtures.MANAGED_POLICY)
701 const evaluations = Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
702
703 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
704
705 expect({ reads: reads(), evaluations: evaluations() }).toEqual({
706 reads: 0,
707 evaluations: 1,
708 })
709 })
710
711 test(
712 'an allow that never called next meets the deny rule all the same',
713 { plugins: [Fixtures.blindAllowing] },
714 async ($, on) => {
715 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
716
717 const lines = Fixtures.logged(on)
718 const evaluations = Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
719
720 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
721
722 expect({ lines, evaluations: evaluations() }).toEqual({
723 lines: [
724 `transcript: ${Hooks.heldNotice('blind', 'Bash', 'Bash(echo *)')}`,
725 ],
726 evaluations: 1,
727 })
728 },
729 )
730
731 test(
732 'a rule a plugin of the person writes into its answer is never read',
733 { plugins: [Fixtures.forging] },
734 async ($, on) => {
735 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
736
737 const lines = Fixtures.logged(on)
738
739 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
740
741 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
742
743 expect(lines).toEqual([
744 `transcript: ${Hooks.heldNotice('forging', 'Bash', 'Bash(echo *)')}`,
745 ])
746 },
747 )
748
749 test(
750 'a plugin of the person asking about another command is left out',
751 { plugins: [Fixtures.rewriting] },
752 async ($, on) => {
753 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
754 Fixtures.logged(on)
755
756 const asked: unknown[] = []
757
758 on('tool.check', ($, e) => {
759 asked.push(e.input)
760
761 return Fixtures.RULE_DENY
762 })
763
764 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
765
766 expect(
767 asked,
768 'the rules were asked about the command that would run, only',
769 ).toEqual([Fixtures.CHECKED.input])
770 },
771 )
772
773 test(
774 'managed settings may let the plugins a person installs override',
775 { plugins: [Fixtures.allowing('easy')] },
776 async ($, on) => {
777 on('settings.read', () => ({ value: Fixtures.overridePolicyOf(true) }))
778
779 const lines = Fixtures.logged(on)
780 const evaluations = Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
781
782 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.ALLOWED)
783
784 expect({ lines, evaluations: evaluations() }).toEqual({
785 lines: [],
786 evaluations: 1,
787 })
788 },
789 )
790
791 test(
792 "only managed settings are asked: a person's own cannot lift a deny rule",
793 { plugins: [Fixtures.allowing('easy')] },
794 async ($, on) => {
795 Fixtures.policyBySource(
796 on,
797 Fixtures.MANAGED_POLICY,
798 Fixtures.overridePolicyOf(true),
799 )
800
801 Fixtures.logged(on)
802 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
803
804 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
805 },
806 )
807
808 test(
809 'the option mistyped lifts nothing: only the literal true does',
810 { plugins: [Fixtures.allowing('easy')] },
811 async ($, on) => {
812 on('settings.read', () => ({ value: Fixtures.overridePolicyOf('true') }))
813 Fixtures.logged(on)
814 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
815
816 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
817 },
818 )
819
820 test(
821 'with a policy that cannot be read the deny rule holds: fails closed',
822 { plugins: [Fixtures.allowing('easy')] },
823 async ($, on) => {
824 const stopPolicy = Fixtures.policyUntilStopped(
825 on,
826 Fixtures.overridePolicyOf(true),
827 'managed settings unreadable',
828 )
829
830 Fixtures.logged(on)
831 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
832 on('prompt.section', ($, e) => ({ text: e.text }))
833
834 await $.prompt.section(Fixtures.MEMORY)
835 stopPolicy()
836
837 expect(
838 await $.tool.check(Fixtures.CHECKED),
839 'the policy that read let plugins override; unread, the rule holds',
840 ).toEqual(Fixtures.RULE_DENY)
841 },
842 )
843
844 test(
845 "when the rules cannot be evaluated the call is refused: the hook's catch",
846 { plugins: [Fixtures.blindAllowing] },
847 async ($, on) => {
848 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
849 Fixtures.logged(on)
850
851 on('tool.check', () => {
852 throw new Error('the evaluation failed')
853 })
854
855 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Hooks.UNCHECKED_DENY)
856 },
857 )
858
859 test(
860 "an organization's ceiling holds over an allow from a plugin of theirs",
861 { plugins: [Fixtures.allowing('easy')] },
862 async ($, on) => {
863 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
864
865 const lines = Fixtures.logged(on)
866 const evaluations = Fixtures.checksAnswered(on, Fixtures.CAPPED_ASK)
867
868 expect(await $.tool.check(Fixtures.CAPPED)).toEqual(Fixtures.CAPPED_ASK)
869 expect(await $.tool.check(Fixtures.CAPPED)).toEqual(Fixtures.CAPPED_ASK)
870
871 expect({ lines, evaluations: evaluations() }).toEqual({
872 lines: [`transcript: ${Hooks.ceilingNotice('easy', Fixtures.CAPPED)}`],
873 evaluations: 4,
874 })
875 },
876 )
877
878 test(
879 'the ceiling holds over an allow that never called next',
880 { plugins: [Fixtures.blindAllowing] },
881 async ($, on) => {
882 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
883 Fixtures.logged(on)
884
885 const evaluations = Fixtures.checksAnswered(on, Fixtures.CAPPED_ASK)
886
887 expect(await $.tool.check(Fixtures.CAPPED)).toEqual(Fixtures.CAPPED_ASK)
888 expect(evaluations()).toBe(1)
889 },
890 )
891
892 test(
893 'the ceiling holds where plugins of theirs may override deny rules',
894 { plugins: [Fixtures.allowing('easy')] },
895 async ($, on) => {
896 on('settings.read', () => ({ value: Fixtures.overridePolicyOf(true) }))
897 Fixtures.logged(on)
898 Fixtures.checksAnswered(on, Fixtures.CAPPED_ASK)
899
900 expect(await $.tool.check(Fixtures.CAPPED)).toEqual(Fixtures.CAPPED_ASK)
901 },
902 )
903
904 test(
905 'there, an allow over a deny rule on such a tool gets the rule back',
906 { plugins: [Fixtures.allowing('easy')] },
907 async ($, on) => {
908 on('settings.read', () => ({ value: Fixtures.overridePolicyOf(true) }))
909
910 const lines = Fixtures.logged(on)
911
912 Fixtures.checksAnswered(on, Fixtures.CAPPED_RULE_DENY)
913
914 expect(await $.tool.check(Fixtures.CAPPED)).toEqual(
915 Fixtures.CAPPED_RULE_DENY,
916 )
917
918 expect(lines).toEqual([
919 `transcript: ${Hooks.ceilingNotice('easy', Fixtures.CAPPED)}`,
920 ])
921 },
922 )
923
924 test(
925 'a deny rule on a tool under a ceiling holds as a deny rule does',
926 { plugins: [Fixtures.allowing('easy')] },
927 async ($, on) => {
928 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
929
930 const lines = Fixtures.logged(on)
931
932 Fixtures.checksAnswered(on, Fixtures.CAPPED_RULE_DENY)
933
934 expect(await $.tool.check(Fixtures.CAPPED)).toEqual(
935 Fixtures.CAPPED_RULE_DENY,
936 )
937
938 expect(lines).toEqual([
939 'transcript: ' +
940 Hooks.heldNotice('easy', Fixtures.CAPPED.tool, 'Bash(echo *)'),
941 ])
942 },
943 )
944
945 test(
946 'an ask from a plugin of theirs is within the ceiling: one evaluation',
947 { plugins: [Fixtures.asking('easy')] },
948 async ($, on) => {
949 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
950
951 const lines = Fixtures.logged(on)
952 const evaluations = Fixtures.checksAnswered(on, Fixtures.CAPPED_ASK)
953
954 expect(await $.tool.check(Fixtures.CAPPED)).toEqual({
955 ...Fixtures.CAPPED_ASK,
956 reason: 'Bash needs approval',
957 })
958
959 expect({ lines, evaluations: evaluations() }).toEqual({
960 lines: [],
961 evaluations: 1,
962 })
963 },
964 )
965
966 test(
967 'a plugin of theirs that tightens under a ceiling is heard',
968 { plugins: [Fixtures.tightening] },
969 async ($, on) => {
970 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
971
972 const evaluations = Fixtures.checksAnswered(on, Fixtures.CAPPED_ASK)
973
974 expect(await $.tool.check(Fixtures.CAPPED)).toEqual({
975 ...Fixtures.PLAIN_DENY,
976 ceiling: 'ask',
977 })
978
979 expect(evaluations()).toBe(1)
980 },
981 )
982
983 test(
984 "an organization plugin's allow over the ceiling stands beneath theirs",
985 {
986 plugins: [
987 Fixtures.allowing('suite', 'append'),
988 Fixtures.allowing('easy'),
989 ],
990 },
991 async ($, on) => {
992 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
993
994 const lines = Fixtures.logged(on)
995
996 Fixtures.checksAnswered(on, Fixtures.CAPPED_ASK)
997
998 expect(await $.tool.check(Fixtures.CAPPED)).toEqual(
999 Fixtures.CAPPED_ALLOWED,
1000 )
1001
1002 expect(lines).toEqual([])
1003 },
1004 )
1005
1006 test(
1007 "a prepended organization plugin's allow over the ceiling stands",
1008 { plugins: [Fixtures.allowing('guard', 'prepend')] },
1009 async ($, on) => {
1010 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1011
1012 const lines = Fixtures.logged(on)
1013
1014 Fixtures.checksAnswered(on, Fixtures.CAPPED_ASK)
1015
1016 expect(await $.tool.check(Fixtures.CAPPED)).toEqual(
1017 Fixtures.CAPPED_ALLOWED,
1018 )
1019
1020 expect(lines).toEqual([])
1021 },
1022 )
1023
1024 test(
1025 "a built-in's allow over the ceiling stands",
1026 { plugins: [Fixtures.allowing('bundled', 'builtin')] },
1027 async ($, on) => {
1028 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1029
1030 const lines = Fixtures.logged(on)
1031
1032 Fixtures.checksAnswered(on, Fixtures.CAPPED_ASK)
1033
1034 expect(await $.tool.check(Fixtures.CAPPED)).toEqual(
1035 Fixtures.CAPPED_ALLOWED,
1036 )
1037
1038 expect(lines).toEqual([])
1039 },
1040 )
1041
1042 test(
1043 'a ceiling a plugin of theirs writes into its answer is never read',
1044 { plugins: [Fixtures.ceilingForging] },
1045 async ($, on) => {
1046 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1047 Fixtures.logged(on)
1048 Fixtures.checksAnswered(on, Fixtures.CAPPED_ASK)
1049
1050 expect(await $.tool.check(Fixtures.CAPPED)).toEqual(Fixtures.CAPPED_ASK)
1051 },
1052 )
1053
1054 test(
1055 'a plugin of theirs asking beneath under another ceiling is left out',
1056 { plugins: [Fixtures.ceilingLifting] },
1057 async ($, on) => {
1058 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1059
1060 const lines = Fixtures.logged(on)
1061
1062 Fixtures.checksAnswered(on, Fixtures.CAPPED_ASK)
1063
1064 expect(await $.tool.check(Fixtures.CAPPED)).toEqual(Fixtures.CAPPED_ASK)
1065 expect(lines.filter(line => line.startsWith('transcript'))).toEqual([])
1066 },
1067 )
1068
1069 test('none of theirs: an ask under a ceiling passes once', async ($, on) => {
1070 const reads = Fixtures.policyReads(on, Fixtures.MANAGED_POLICY)
1071 const evaluations = Fixtures.checksAnswered(on, Fixtures.CAPPED_ASK)
1072
1073 expect(await $.tool.check(Fixtures.CAPPED)).toEqual(Fixtures.CAPPED_ASK)
1074
1075 expect({ reads: reads(), evaluations: evaluations() }).toEqual({
1076 reads: 0,
1077 evaluations: 1,
1078 })
1079 })
1080
1081 test(
1082 "when the run past theirs rejects so does the call: the hook's catch",
1083 { plugins: [Fixtures.blindAllowing] },
1084 async ($, on) => {
1085 on('settings.read', () => ({ value: Fixtures.overridePolicyOf(true) }))
1086 Fixtures.logged(on)
1087
1088 on('tool.check', () => {
1089 throw new Error('the evaluation failed')
1090 })
1091
1092 await expect($.tool.check(Fixtures.CAPPED)).rejects.toThrow('tool.check')
1093 },
1094 )
1095
1096 test(
1097 'a failed check of deny rules on such a tool is still a refusal',
1098 { plugins: [Fixtures.blindAllowing] },
1099 async ($, on) => {
1100 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1101 Fixtures.logged(on)
1102
1103 on('tool.check', () => {
1104 throw new Error('the evaluation failed')
1105 })
1106
1107 expect(await $.tool.check(Fixtures.CAPPED)).toEqual({
1108 ...Hooks.UNCHECKED_DENY,
1109 ceiling: 'ask',
1110 })
1111 },
1112 )
1113
1114 test(
1115 'only a plugin of theirs that answered over the ceiling is told of it',
1116 { plugins: [Fixtures.allowing('easy'), Fixtures.asking('soft')] },
1117 async ($, on) => {
1118 on('settings.read', () => ({ value: Fixtures.overridePolicyOf(true) }))
1119
1120 const lines = Fixtures.logged(on)
1121
1122 Fixtures.checksAnswered(on, { ...Fixtures.PLAIN_DENY, ceiling: 'ask' })
1123
1124 expect(await $.tool.check(Fixtures.CAPPED)).toEqual({
1125 ...Fixtures.PLAIN_DENY,
1126 ceiling: 'ask',
1127 })
1128
1129 expect(lines).toEqual([
1130 `transcript: ${Hooks.ceilingNotice('easy', Fixtures.CAPPED)}`,
1131 ])
1132 },
1133 )
1134
1135 test(
1136 'the line it logs passes over a plugin of theirs that hooks ui.log',
1137 { plugins: [Fixtures.allowing('easy'), Fixtures.logSwallowing] },
1138 async ($, on) => {
1139 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1140
1141 const lines = Fixtures.logged(on)
1142
1143 Fixtures.checksAnswered(on, Fixtures.CAPPED_ASK)
1144
1145 expect(await $.tool.check(Fixtures.CAPPED)).toEqual(Fixtures.CAPPED_ASK)
1146
1147 expect(lines).toEqual([
1148 `transcript: ${Hooks.ceilingNotice('easy', Fixtures.CAPPED)}`,
1149 ])
1150 },
1151 )
1152
1153 test(
1154 'the deny rule line passes over such a plugin of theirs too',
1155 { plugins: [Fixtures.allowing('easy'), Fixtures.logSwallowing] },
1156 async ($, on) => {
1157 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1158
1159 const lines = Fixtures.logged(on)
1160
1161 Fixtures.checksAnswered(on, Fixtures.RULE_DENY)
1162
1163 expect(await $.tool.check(Fixtures.CHECKED)).toEqual(Fixtures.RULE_DENY)
1164
1165 expect(lines).toEqual([
1166 `transcript: ${Hooks.heldNotice('easy', 'Bash', 'Bash(echo *)')}`,
1167 ])
1168 },
1169 )
1170
1171 test(
1172 'what a plugin of theirs logs is still theirs to hook',
1173 { plugins: [Fixtures.logging('chatty'), Fixtures.logSwallowing] },
1174 async ($, on) => {
1175 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1176
1177 const lines = Fixtures.logged(on)
1178
1179 Fixtures.checksAnswered(on, Fixtures.ASKED)
1180
1181 await $.tool.check(Fixtures.CHECKED)
1182
1183 expect(lines).toEqual([])
1184 },
1185 )
1186
1187 test(
1188 "what an organization's plugin logs passes over theirs",
1189 {
1190 plugins: [Fixtures.logging('suite', 'append'), Fixtures.logSwallowing],
1191 },
1192 async ($, on) => {
1193 on('settings.read', () => ({ value: Fixtures.MANAGED_POLICY }))
1194
1195 const lines = Fixtures.logged(on)
1196
1197 Fixtures.checksAnswered(on, Fixtures.ASKED)
1198
1199 await $.tool.check(Fixtures.CHECKED)
1200
1201 expect(lines).toEqual(['transcript: heard a tool.check'])
1202 },
1203 )
1204})