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 / held-verdict.test.ts
GitHub Actions (publish-mods)chore: Update mods/ 5a2676e · 2 days ago History
245 lines · 7.6 KBRaw
1import { describe, expect, test, tier } from 'claude-code/testing'
2
3import Hooks from '../hooks'
4import Fixtures from './fixtures'
5
6tier('prepend')
7
8describe('held-verdict', () => {
9 test('over an allow: any deny, and an ask a rule or a hook decided', () => {
10 expect(
11 [
12 Fixtures.RULE_DENY,
13 Fixtures.RULE_ASK,
14 Fixtures.HOOK_DENY,
15 Fixtures.HOOK_ASK,
16 Fixtures.PLAIN_DENY,
17 Fixtures.ASKED,
18 Fixtures.ALLOWED,
19 ].map(held => Hooks.holdsOver(held, Fixtures.ALLOWED, undefined)),
20 ).toEqual([true, true, true, true, true, false, false])
21 })
22
23 test("a setting's ask and a host's hold over an allow and a rewording", () => {
24 expect(
25 [Fixtures.SETTING_ASK, Fixtures.HOST_ASK].map(held => [
26 Hooks.holdsOver(held, Fixtures.ALLOWED, 'host'),
27 Hooks.holdsOver(held, Fixtures.ASKED, 'host'),
28 Hooks.holdsOver(held, { ...held }, 'host'),
29 ]),
30 ).toEqual([
31 [true, true, false],
32 [true, true, false],
33 ])
34 })
35
36 test('a setting the answer itself names holds nothing', () => {
37 expect(
38 Hooks.holdsOver(
39 Fixtures.ASKED,
40 {
41 ...Fixtures.ALLOWED,
42 setting: 'permissions.blockReadsOutsideWorkingDirectories',
43 },
44 undefined,
45 ),
46 ).toBe(false)
47 })
48
49 test('an ask is named for its hook, then its setting, then its host', () => {
50 expect(
51 [
52 { ...Fixtures.HOST_ASK, ...Fixtures.SETTING_ASK, ...Fixtures.HOOK_ASK },
53 { ...Fixtures.HOST_ASK, ...Fixtures.SETTING_ASK },
54 Fixtures.HOST_ASK,
55 ].map(held => Hooks.heldNotice('easy', 'Bash', held, 'lift')),
56 ).toEqual(
57 [
58 "a PreToolUse hook's ask from a Bash call; the PreToolUse hook's ask",
59 "a permissions.blockReadsOutsideWorkingDirectories setting's ask " +
60 'from a Bash call; the ' +
61 "permissions.blockReadsOutsideWorkingDirectories setting's ask",
62 "a host's ask from a Bash call; the host's ask",
63 ].map(
64 middle =>
65 `easy tried to lift ${middle} holds over the plugins you install ` +
66 '(allowModsToOverrideDenyRules)',
67 ),
68 )
69 })
70
71 test('over the same decision reworded; a stricter answer stands', () => {
72 expect(
73 [
74 Fixtures.RULE_DENY,
75 Fixtures.RULE_ASK,
76 Fixtures.HOOK_DENY,
77 Fixtures.HOOK_ASK,
78 ].map(held => [
79 Hooks.holdsOver(held, Fixtures.ASKED, undefined),
80 Hooks.holdsOver(held, Fixtures.PLAIN_DENY, undefined),
81 Hooks.holdsOver(held, { ...held }, undefined),
82 ]),
83 ).toEqual([
84 [true, true, false],
85 [true, false, false],
86 [true, true, false],
87 [true, false, false],
88 ])
89 })
90
91 test('an ask stripped of its rule, same sentence, is no same verdict', () => {
92 expect(
93 Hooks.holdsOver(
94 Fixtures.RULE_ASK,
95 {
96 decision: Fixtures.RULE_ASK.decision,
97 reason: Fixtures.RULE_ASK.reason,
98 },
99 undefined,
100 ),
101 ).toBe(true)
102 })
103
104 test('a rule or hook the answer itself names makes nothing hold', () => {
105 expect(
106 Hooks.holdsOver(
107 Fixtures.ASKED,
108 {
109 ...Fixtures.ALLOWED,
110 rule: 'Bash(echo *)',
111 hook: 'PreToolUse',
112 },
113 undefined,
114 ),
115 ).toBe(false)
116 })
117
118 test('the notice names the kind that held, and the rule when one did', () => {
119 expect(
120 [
121 Fixtures.RULE_DENY,
122 Fixtures.RULE_ASK,
123 { ...Fixtures.RULE_ASK, ...Fixtures.HOOK_ASK },
124 Fixtures.HOOK_ASK,
125 Fixtures.HOOK_DENY,
126 Fixtures.PLAIN_DENY,
127 ].map(held => Hooks.heldNotice('easy', 'Bash', held, 'lift')),
128 ).toEqual(
129 [
130 'a deny rule in your settings from a Bash call (Bash(echo *)); ' +
131 'the deny rule',
132 'an ask rule in your settings from a Bash call (Bash(echo *)); ' +
133 'the ask rule',
134 'an ask rule in your settings from a Bash call (Bash(echo *)); ' +
135 'the ask rule',
136 "a PreToolUse hook's ask from a Bash call; the PreToolUse hook's ask",
137 'a refusal from a Bash call; the refusal',
138 'a refusal from a Bash call; the refusal',
139 ].map(
140 middle =>
141 `easy tried to lift ${middle} holds over the plugins you install ` +
142 '(allowModsToOverrideDenyRules)',
143 ),
144 )
145 })
146
147 test('what no check vouches for is refused, and says why', () => {
148 expect(Hooks.UNCHECKED_DENY).toEqual({
149 decision: 'deny',
150 reason:
151 'the rules and hooks in your settings could not be checked for this ' +
152 'call, so it is refused',
153 })
154 })
155
156 test('an ask of theirs over a deny, caught, is no more vouched for', () => {
157 expect(
158 Hooks.caughtAnswer(Fixtures.ASKED, [
159 Fixtures.linkOf('easy', 'user', Fixtures.ASKED),
160 Fixtures.linkOf('engine', 'core', Fixtures.RULE_DENY),
161 ]),
162 ).toEqual(Hooks.UNCHECKED_DENY)
163 })
164
165 test('a link of theirs is named when it answered looser than handed', () => {
166 expect(
167 Hooks.rewrittenByUsers([
168 Fixtures.linkOf('listening', 'user', Fixtures.ALLOWED),
169 Fixtures.linkOf('easy', 'user', Fixtures.ALLOWED),
170 Fixtures.linkOf('engine', 'core', Fixtures.ASKED),
171 ]),
172 ).toEqual(['easy'])
173 })
174
175 test('one that never called next is measured against a deny', () => {
176 expect(
177 Hooks.rewrittenByUsers([
178 Fixtures.linkOf('listening', 'user', Fixtures.ALLOWED),
179 Fixtures.linkOf('blind', 'user', Fixtures.ALLOWED),
180 ]),
181 ).toEqual(['blind'])
182 })
183
184 test('one that reworded the same decision is named too', () => {
185 expect(
186 Hooks.rewrittenByUsers([
187 Fixtures.linkOf('reworder', 'user', Fixtures.ASKED),
188 Fixtures.linkOf('engine', 'core', Fixtures.RULE_ASK),
189 ]),
190 ).toEqual(['reworder'])
191 })
192
193 test('a tightening, a skipped link and links beneath theirs are not', () => {
194 expect(
195 Hooks.rewrittenByUsers([
196 Fixtures.linkOf('strict', 'user', Fixtures.PLAIN_DENY),
197 Fixtures.linkOf('passed-over', 'user'),
198 Fixtures.linkOf('suite', 'append', Fixtures.ALLOWED),
199 Fixtures.linkOf('bundled', 'builtin', Fixtures.ALLOWED),
200 Fixtures.linkOf('engine', 'core', Fixtures.RULE_DENY),
201 ]),
202 ).toEqual([])
203 })
204
205 test('a batch listed under prepend may hold a plugin of theirs', () => {
206 expect(
207 Hooks.rewrittenByUsers([
208 Fixtures.linkOf('audit+easy', 'prepend', Fixtures.ALLOWED),
209 Fixtures.linkOf('engine', 'core', Fixtures.RULE_DENY),
210 ]),
211 ).toEqual(['audit+easy'])
212 })
213
214 test('the handler keeps a verdict none of theirs made, or tightened', () => {
215 expect([
216 Hooks.caughtAnswer(Fixtures.PLAIN_DENY, [
217 Fixtures.linkOf('strict', 'user', Fixtures.PLAIN_DENY),
218 Fixtures.linkOf('engine', 'core', Fixtures.ASKED),
219 ]),
220 Hooks.caughtAnswer(Fixtures.ASKED, [
221 Fixtures.linkOf('easy', 'user'),
222 Fixtures.linkOf('engine', 'core', Fixtures.ASKED),
223 ]),
224 ]).toEqual([Fixtures.PLAIN_DENY, Fixtures.ASKED])
225 })
226
227 test('a deny of the rules they reworded is refused in our words', () => {
228 expect(
229 Hooks.caughtAnswer(Fixtures.PLAIN_DENY, [
230 Fixtures.linkOf('reworder', 'user', Fixtures.PLAIN_DENY),
231 Fixtures.linkOf('engine', 'core', Fixtures.RULE_DENY),
232 ]),
233 ).toEqual(Hooks.UNCHECKED_DENY)
234 })
235
236 test('the handler refuses a verdict they loosened, and no verdict', () => {
237 expect([
238 Hooks.caughtAnswer(Fixtures.ALLOWED, [
239 Fixtures.linkOf('easy', 'user', Fixtures.ALLOWED),
240 Fixtures.linkOf('engine', 'core', Fixtures.ASKED),
241 ]),
242 Hooks.caughtAnswer(undefined, []),
243 ]).toEqual([Hooks.UNCHECKED_DENY, Hooks.UNCHECKED_DENY])
244 })
245})