-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest-hook-trust.mjs
More file actions
126 lines (114 loc) · 4.77 KB
/
Copy pathtest-hook-trust.mjs
File metadata and controls
126 lines (114 loc) · 4.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// Test the project-hook trust gate. Run with: node test-hook-trust.mjs
import { loadSettings, getPendingProjectHooks, trustProjectHooks } from "./dist/config/settings.js"
import assert from "node:assert"
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs"
import { tmpdir } from "node:os"
import { join } from "node:path"
let passed = 0
const ok = (name) => { console.log(` ok - ${name}`); passed++ }
const PROJECT_HOOKS = {
hooks: {
PreToolUse: [
{ matcher: "execute_command", hooks: [{ type: "command", command: "echo from-project-hook" }] },
],
},
}
function fresh() {
const cfg = mkdtempSync(join(tmpdir(), "orb-cfg-"))
const proj = mkdtempSync(join(tmpdir(), "orb-proj-"))
process.env.MATTERAI_CONFIG_DIR = cfg
delete process.env.MATTERAI_TRUST_PROJECT_HOOKS
process.chdir(proj)
return { cfg, cwd: process.cwd() }
}
function writeProjectHooks(cwd, obj) {
mkdirSync(join(cwd, ".orbcode"), { recursive: true })
writeFileSync(join(cwd, ".orbcode", "settings.json"), JSON.stringify(obj))
}
// 1. Untrusted project hooks are NOT loaded, and are reported as pending.
{
const { cwd } = fresh()
writeProjectHooks(cwd, PROJECT_HOOKS)
const s = loadSettings()
assert.strictEqual(s.hooks, undefined, "untrusted project hooks must not be active")
const pending = getPendingProjectHooks()
assert.ok(pending, "pending project hooks reported")
assert.deepEqual(pending.commands, ["echo from-project-hook"])
ok("untrusted project hooks are disabled and reported as pending")
}
// 2. After trusting, project hooks load and nothing is pending.
{
const { cwd } = fresh()
writeProjectHooks(cwd, PROJECT_HOOKS)
trustProjectHooks()
const s = loadSettings()
assert.ok(s.hooks?.PreToolUse, "trusted project hooks become active")
assert.strictEqual(s.hooks.PreToolUse[0].hooks[0].command, "echo from-project-hook")
assert.strictEqual(getPendingProjectHooks(), null, "nothing pending once trusted")
ok("trusting enables project hooks")
}
// 3. Changing the hooks invalidates trust (re-prompt) and disables them again.
{
const { cwd } = fresh()
writeProjectHooks(cwd, PROJECT_HOOKS)
trustProjectHooks()
assert.strictEqual(getPendingProjectHooks(), null, "trusted before edit")
// Edit the hooks: trust must no longer apply.
writeProjectHooks(cwd, {
hooks: { PreToolUse: [{ hooks: [{ type: "command", command: "echo SOMETHING-NEW" }] }] },
})
const pending = getPendingProjectHooks()
assert.ok(pending, "edited hooks are pending again")
assert.deepEqual(pending.commands, ["echo SOMETHING-NEW"])
assert.strictEqual(loadSettings().hooks, undefined, "edited hooks are disabled until re-trusted")
ok("editing project hooks re-prompts (trust is content-hashed)")
}
// 4. MATTERAI_TRUST_PROJECT_HOOKS=1 trusts project hooks without prompting (CI).
{
const { cwd } = fresh()
writeProjectHooks(cwd, PROJECT_HOOKS)
process.env.MATTERAI_TRUST_PROJECT_HOOKS = "1"
assert.strictEqual(getPendingProjectHooks(), null, "env override => nothing pending")
assert.ok(loadSettings().hooks?.PreToolUse, "env override => hooks active")
delete process.env.MATTERAI_TRUST_PROJECT_HOOKS
ok("MATTERAI_TRUST_PROJECT_HOOKS=1 enables project hooks (CI escape hatch)")
}
// 5. User-level hooks are ALWAYS active regardless of project trust.
{
const { cfg, cwd } = fresh()
writeFileSync(
join(cfg, "settings.json"),
JSON.stringify({ hooks: { Stop: [{ hooks: [{ type: "command", command: "echo user-hook" }] }] } }),
)
writeProjectHooks(cwd, PROJECT_HOOKS) // untrusted
const s = loadSettings()
assert.ok(s.hooks?.Stop, "user hooks active")
assert.strictEqual(s.hooks.PreToolUse, undefined, "untrusted project hooks still excluded")
ok("user hooks always apply; project hooks gated independently")
}
// 6. Trusted project hooks concatenate with user hooks (don't clobber).
{
const { cfg, cwd } = fresh()
writeFileSync(
join(cfg, "settings.json"),
JSON.stringify({ hooks: { PreToolUse: [{ hooks: [{ type: "command", command: "echo user" }] }] } }),
)
writeProjectHooks(cwd, PROJECT_HOOKS)
trustProjectHooks()
const s = loadSettings()
const cmds = s.hooks.PreToolUse.flatMap((m) => m.hooks.map((h) => h.command))
assert.deepEqual(cmds, ["echo user", "echo from-project-hook"], "user then project, merged")
ok("trusted project hooks merge with (don't clobber) user hooks")
}
// 7. MATTERAI_TRUST_PROJECT_HOOKS only honors the exact value "1" (not "true").
{
const { cwd } = fresh()
writeProjectHooks(cwd, PROJECT_HOOKS)
process.env.MATTERAI_TRUST_PROJECT_HOOKS = "true"
const pending = getPendingProjectHooks()
assert.ok(pending, '"true" must not be honored — only "1"')
assert.strictEqual(loadSettings().hooks, undefined, '"true" must not enable hooks')
delete process.env.MATTERAI_TRUST_PROJECT_HOOKS
ok('MATTERAI_TRUST_PROJECT_HOOKS="true" is not honored (strict "1" only)')
}
console.log(`\n${passed} checks passed`)