@namzu/cli 12.1.0 → 13.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +119 -60
- package/dist/cli.js.map +1 -1
- package/dist/commands/acp.d.ts +24 -8
- package/dist/commands/acp.d.ts.map +1 -1
- package/dist/commands/acp.js +221 -80
- package/dist/commands/acp.js.map +1 -1
- package/dist/commands/drain.d.ts.map +1 -1
- package/dist/commands/drain.js +45 -9
- package/dist/commands/drain.js.map +1 -1
- package/dist/commands/prior-messages.d.ts +11 -0
- package/dist/commands/prior-messages.d.ts.map +1 -0
- package/dist/commands/prior-messages.js +280 -0
- package/dist/commands/prior-messages.js.map +1 -0
- package/dist/commands/resume.d.ts.map +1 -1
- package/dist/commands/resume.js +21 -19
- package/dist/commands/resume.js.map +1 -1
- package/dist/commands/run-stream.d.ts +5 -3
- package/dist/commands/run-stream.d.ts.map +1 -1
- package/dist/commands/run-stream.js +66 -62
- package/dist/commands/run-stream.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +50 -37
- package/dist/commands/run.js.map +1 -1
- package/dist/config/debug.d.ts +55 -0
- package/dist/config/debug.d.ts.map +1 -0
- package/dist/config/debug.js +126 -0
- package/dist/config/debug.js.map +1 -0
- package/dist/config/load.d.ts +88 -4
- package/dist/config/load.d.ts.map +1 -1
- package/dist/config/load.js +386 -56
- package/dist/config/load.js.map +1 -1
- package/dist/config/schema.d.ts +87 -5
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +5 -5
- package/dist/config/schema.js.map +1 -1
- package/dist/config/trusted-project-context.d.ts +11 -0
- package/dist/config/trusted-project-context.d.ts.map +1 -0
- package/dist/config/trusted-project-context.js +28 -0
- package/dist/config/trusted-project-context.js.map +1 -0
- package/dist/context/project-path.d.ts +3 -0
- package/dist/context/project-path.d.ts.map +1 -0
- package/dist/context/project-path.js +16 -0
- package/dist/context/project-path.js.map +1 -0
- package/dist/context/project-tracker.d.ts +30 -0
- package/dist/context/project-tracker.d.ts.map +1 -0
- package/dist/context/project-tracker.js +217 -0
- package/dist/context/project-tracker.js.map +1 -0
- package/dist/context/sandbox.d.ts +29 -9
- package/dist/context/sandbox.d.ts.map +1 -1
- package/dist/context/sandbox.js +8 -0
- package/dist/context/sandbox.js.map +1 -1
- package/dist/exit-codes.d.ts +5 -4
- package/dist/exit-codes.d.ts.map +1 -1
- package/dist/exit-codes.js +5 -4
- package/dist/exit-codes.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/integrations/clipboard/text.d.ts +43 -0
- package/dist/integrations/clipboard/text.d.ts.map +1 -0
- package/dist/integrations/clipboard/text.js +43 -0
- package/dist/integrations/clipboard/text.js.map +1 -0
- package/dist/integrations/mcp/servers.d.ts +9 -0
- package/dist/integrations/mcp/servers.d.ts.map +1 -1
- package/dist/integrations/mcp/servers.js +11 -4
- package/dist/integrations/mcp/servers.js.map +1 -1
- package/dist/integrations/notifications/terminal.d.ts +32 -0
- package/dist/integrations/notifications/terminal.d.ts.map +1 -0
- package/dist/integrations/notifications/terminal.js +46 -0
- package/dist/integrations/notifications/terminal.js.map +1 -0
- package/dist/integrations/plugins/runtime.d.ts +17 -0
- package/dist/integrations/plugins/runtime.d.ts.map +1 -0
- package/dist/integrations/plugins/runtime.js +100 -0
- package/dist/integrations/plugins/runtime.js.map +1 -0
- package/dist/integrations/providers/credential-store.d.ts +17 -0
- package/dist/integrations/providers/credential-store.d.ts.map +1 -1
- package/dist/integrations/providers/credential-store.js +114 -2
- package/dist/integrations/providers/credential-store.js.map +1 -1
- package/dist/integrations/providers/index.d.ts +2 -2
- package/dist/integrations/providers/index.d.ts.map +1 -1
- package/dist/integrations/providers/index.js +2 -2
- package/dist/integrations/providers/index.js.map +1 -1
- package/dist/integrations/providers/keychain.d.ts +0 -13
- package/dist/integrations/providers/keychain.d.ts.map +1 -1
- package/dist/integrations/providers/keychain.js +0 -76
- package/dist/integrations/providers/keychain.js.map +1 -1
- package/dist/integrations/providers/oauth.d.ts +45 -16
- package/dist/integrations/providers/oauth.d.ts.map +1 -1
- package/dist/integrations/providers/oauth.js +170 -43
- package/dist/integrations/providers/oauth.js.map +1 -1
- package/dist/integrations/providers/subscription-login.d.ts +2 -0
- package/dist/integrations/providers/subscription-login.d.ts.map +1 -1
- package/dist/integrations/providers/subscription-login.js +117 -14
- package/dist/integrations/providers/subscription-login.js.map +1 -1
- package/dist/integrations/sessions/store.d.ts +111 -1
- package/dist/integrations/sessions/store.d.ts.map +1 -1
- package/dist/integrations/sessions/store.js +400 -7
- package/dist/integrations/sessions/store.js.map +1 -1
- package/dist/integrations/sessions/transcript-export.d.ts +25 -0
- package/dist/integrations/sessions/transcript-export.d.ts.map +1 -0
- package/dist/integrations/sessions/transcript-export.js +441 -0
- package/dist/integrations/sessions/transcript-export.js.map +1 -0
- package/dist/integrations/sessions/turn-evidence.d.ts +138 -0
- package/dist/integrations/sessions/turn-evidence.d.ts.map +1 -0
- package/dist/integrations/sessions/turn-evidence.js +506 -0
- package/dist/integrations/sessions/turn-evidence.js.map +1 -0
- package/dist/integrations/subagents/runtime.d.ts +7 -12
- package/dist/integrations/subagents/runtime.d.ts.map +1 -1
- package/dist/integrations/subagents/runtime.js +19 -10
- package/dist/integrations/subagents/runtime.js.map +1 -1
- package/dist/integrations/trust/store.d.ts.map +1 -1
- package/dist/integrations/trust/store.js +27 -13
- package/dist/integrations/trust/store.js.map +1 -1
- package/dist/permissions/canonical-project.d.ts +9 -0
- package/dist/permissions/canonical-project.d.ts.map +1 -0
- package/dist/permissions/canonical-project.js +13 -0
- package/dist/permissions/canonical-project.js.map +1 -0
- package/dist/permissions/checks.d.ts +65 -0
- package/dist/permissions/checks.d.ts.map +1 -0
- package/dist/permissions/checks.js +122 -0
- package/dist/permissions/checks.js.map +1 -0
- package/dist/permissions/headless-trust.d.ts +9 -5
- package/dist/permissions/headless-trust.d.ts.map +1 -1
- package/dist/permissions/headless-trust.js +18 -4
- package/dist/permissions/headless-trust.js.map +1 -1
- package/dist/permissions/rules.d.ts +16 -7
- package/dist/permissions/rules.d.ts.map +1 -1
- package/dist/permissions/rules.js +78 -13
- package/dist/permissions/rules.js.map +1 -1
- package/dist/tui/App.d.ts +5 -2
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/App.js +1555 -143
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/Composer.d.ts +14 -3
- package/dist/tui/Composer.d.ts.map +1 -1
- package/dist/tui/Composer.js +53 -15
- package/dist/tui/Composer.js.map +1 -1
- package/dist/tui/EditPromptPicker.d.ts +8 -0
- package/dist/tui/EditPromptPicker.d.ts.map +1 -0
- package/dist/tui/EditPromptPicker.js +18 -0
- package/dist/tui/EditPromptPicker.js.map +1 -0
- package/dist/tui/LiveActivity.d.ts.map +1 -1
- package/dist/tui/LiveActivity.js +2 -1
- package/dist/tui/LiveActivity.js.map +1 -1
- package/dist/tui/Markdown.d.ts.map +1 -1
- package/dist/tui/Markdown.js +2 -1
- package/dist/tui/Markdown.js.map +1 -1
- package/dist/tui/PermissionOverlay.d.ts.map +1 -1
- package/dist/tui/PermissionOverlay.js +7 -1
- package/dist/tui/PermissionOverlay.js.map +1 -1
- package/dist/tui/Picker.d.ts +4 -4
- package/dist/tui/Picker.d.ts.map +1 -1
- package/dist/tui/Picker.js +86 -7
- package/dist/tui/Picker.js.map +1 -1
- package/dist/tui/ResumePicker.d.ts.map +1 -1
- package/dist/tui/ResumePicker.js +1 -1
- package/dist/tui/ResumePicker.js.map +1 -1
- package/dist/tui/Transcript.d.ts +4 -2
- package/dist/tui/Transcript.d.ts.map +1 -1
- package/dist/tui/Transcript.js +31 -5
- package/dist/tui/Transcript.js.map +1 -1
- package/dist/tui/agent.d.ts +123 -19
- package/dist/tui/agent.d.ts.map +1 -1
- package/dist/tui/agent.js +665 -143
- package/dist/tui/agent.js.map +1 -1
- package/dist/tui/compact-transcript.d.ts +24 -0
- package/dist/tui/compact-transcript.d.ts.map +1 -0
- package/dist/tui/compact-transcript.js +46 -0
- package/dist/tui/compact-transcript.js.map +1 -0
- package/dist/tui/conversation-history.d.ts +33 -0
- package/dist/tui/conversation-history.d.ts.map +1 -0
- package/dist/tui/conversation-history.js +42 -0
- package/dist/tui/conversation-history.js.map +1 -0
- package/dist/tui/edit-prompts.d.ts +23 -0
- package/dist/tui/edit-prompts.d.ts.map +1 -0
- package/dist/tui/edit-prompts.js +35 -0
- package/dist/tui/edit-prompts.js.map +1 -0
- package/dist/tui/live-window.d.ts +3 -1
- package/dist/tui/live-window.d.ts.map +1 -1
- package/dist/tui/live-window.js +20 -8
- package/dist/tui/live-window.js.map +1 -1
- package/dist/tui/slashCommands.d.ts +178 -9
- package/dist/tui/slashCommands.d.ts.map +1 -1
- package/dist/tui/slashCommands.js +339 -15
- package/dist/tui/slashCommands.js.map +1 -1
- package/dist/tui/terminal-display.d.ts +12 -0
- package/dist/tui/terminal-display.d.ts.map +1 -0
- package/dist/tui/terminal-display.js +65 -0
- package/dist/tui/terminal-display.js.map +1 -0
- package/dist/tui/types.d.ts +14 -2
- package/dist/tui/types.d.ts.map +1 -1
- package/dist/tui/workspace-diff.d.ts +40 -0
- package/dist/tui/workspace-diff.d.ts.map +1 -0
- package/dist/tui/workspace-diff.js +101 -0
- package/dist/tui/workspace-diff.js.map +1 -0
- package/package.json +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../src/integrations/subagents/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAGN,YAAY,EACZ,aAAa,EAMb,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,EAElB,kBAAkB,
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../src/integrations/subagents/runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/integrations/trust/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/integrations/trust/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA4BH,wBAAgB,aAAa,CAAC,IAAI,GAAE,MAAkB,GAAG,MAAM,CAE9D;AAED,wBAAgB,eAAe,CAAC,IAAI,GAAE,MAAkB,GAAG,MAAM,EAAE,CAOlE;AAED,8DAA8D;AAC9D,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,MAAkB,GAAG,OAAO,CAcxE;AAED,kDAAkD;AAClD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,MAAkB,GAAG,IAAI,CAapE"}
|
|
@@ -7,24 +7,27 @@
|
|
|
7
7
|
* counts as trusted if it — or any ancestor — has been trusted, so
|
|
8
8
|
* trusting a repo root covers its subfolders.
|
|
9
9
|
*/
|
|
10
|
-
import { mkdirSync, readFileSync,
|
|
10
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
11
11
|
import { homedir } from 'node:os';
|
|
12
12
|
import { dirname, join, resolve, sep } from 'node:path';
|
|
13
|
+
import { canonicalProjectPath } from '../../permissions/canonical-project.js';
|
|
13
14
|
const DIR_MODE = 0o700;
|
|
14
15
|
const FILE_MODE = 0o600;
|
|
15
16
|
const TRUST_FILE_VERSION = 1;
|
|
16
|
-
|
|
17
|
-
return join(home, '.namzu', 'trust.json');
|
|
18
|
-
}
|
|
19
|
-
/** Resolve symlinks + normalize so the same dir isn't trusted twice. */
|
|
20
|
-
function canonical(dir) {
|
|
17
|
+
function canonicalStoredPath(dir) {
|
|
21
18
|
try {
|
|
22
|
-
return
|
|
19
|
+
return canonicalProjectPath(dir);
|
|
23
20
|
}
|
|
24
21
|
catch {
|
|
22
|
+
// A remembered checkout may have been removed. Keeping its normalized
|
|
23
|
+
// name readable does not admit a live project; the target below must
|
|
24
|
+
// resolve successfully before it can match.
|
|
25
25
|
return resolve(dir);
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
+
export function trustFilePath(home = homedir()) {
|
|
29
|
+
return join(home, '.namzu', 'trust.json');
|
|
30
|
+
}
|
|
28
31
|
export function readTrustedDirs(home = homedir()) {
|
|
29
32
|
try {
|
|
30
33
|
const parsed = JSON.parse(readFileSync(trustFilePath(home), 'utf8'));
|
|
@@ -36,8 +39,14 @@ export function readTrustedDirs(home = homedir()) {
|
|
|
36
39
|
}
|
|
37
40
|
/** True when `dir` or any ancestor is in the trusted list. */
|
|
38
41
|
export function isTrusted(dir, home = homedir()) {
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
let target;
|
|
43
|
+
try {
|
|
44
|
+
target = canonicalProjectPath(dir);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
const trusted = readTrustedDirs(home).map(canonicalStoredPath);
|
|
41
50
|
for (const t of trusted) {
|
|
42
51
|
if (target === t || target.startsWith(t.endsWith(sep) ? t : t + sep)) {
|
|
43
52
|
return true;
|
|
@@ -47,13 +56,18 @@ export function isTrusted(dir, home = homedir()) {
|
|
|
47
56
|
}
|
|
48
57
|
/** Add `dir` to the trusted list (idempotent). */
|
|
49
58
|
export function trustDir(dir, home = homedir()) {
|
|
50
|
-
const target =
|
|
59
|
+
const target = canonicalProjectPath(dir);
|
|
51
60
|
const current = readTrustedDirs(home);
|
|
52
|
-
if (current.map(
|
|
61
|
+
if (current.map(canonicalStoredPath).includes(target))
|
|
53
62
|
return;
|
|
54
|
-
const next = {
|
|
63
|
+
const next = {
|
|
64
|
+
version: TRUST_FILE_VERSION,
|
|
65
|
+
trusted: [...current, target],
|
|
66
|
+
};
|
|
55
67
|
const path = trustFilePath(home);
|
|
56
68
|
mkdirSync(dirname(path), { recursive: true, mode: DIR_MODE });
|
|
57
|
-
writeFileSync(path, `${JSON.stringify(next, null, 2)}\n`, {
|
|
69
|
+
writeFileSync(path, `${JSON.stringify(next, null, 2)}\n`, {
|
|
70
|
+
mode: FILE_MODE,
|
|
71
|
+
});
|
|
58
72
|
}
|
|
59
73
|
//# sourceMappingURL=store.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/integrations/trust/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/integrations/trust/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAEvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAA;AAE7E,MAAM,QAAQ,GAAG,KAAK,CAAA;AACtB,MAAM,SAAS,GAAG,KAAK,CAAA;AACvB,MAAM,kBAAkB,GAAG,CAAC,CAAA;AAE5B,SAAS,mBAAmB,CAAC,GAAW;IACvC,IAAI,CAAC;QACJ,OAAO,oBAAoB,CAAC,GAAG,CAAC,CAAA;IACjC,CAAC;IAAC,MAAM,CAAC;QACR,sEAAsE;QACtE,qEAAqE;QACrE,4CAA4C;QAC5C,OAAO,OAAO,CAAC,GAAG,CAAC,CAAA;IACpB,CAAC;AACF,CAAC;AAOD,MAAM,UAAU,aAAa,CAAC,OAAe,OAAO,EAAE;IACrD,OAAO,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAA;AAC1C,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAe,OAAO,EAAE;IACvD,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAuB,CAAA;QAC1F,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAChG,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,EAAE,CAAA;IACV,CAAC;AACF,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,SAAS,CAAC,GAAW,EAAE,OAAe,OAAO,EAAE;IAC9D,IAAI,MAAc,CAAA;IAClB,IAAI,CAAC;QACJ,MAAM,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAA;IACnC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAA;IACb,CAAC;IACD,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAA;IAC9D,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACzB,IAAI,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;YACtE,OAAO,IAAI,CAAA;QACZ,CAAC;IACF,CAAC;IACD,OAAO,KAAK,CAAA;AACb,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,QAAQ,CAAC,GAAW,EAAE,OAAe,OAAO,EAAE;IAC7D,MAAM,MAAM,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAA;IACxC,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,CAAC,CAAA;IACrC,IAAI,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAM;IAC7D,MAAM,IAAI,GAAc;QACvB,OAAO,EAAE,kBAAkB;QAC3B,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,MAAM,CAAC;KAC7B,CAAA;IACD,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,CAAA;IAChC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;IAC7D,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;QACzD,IAAI,EAAE,SAAS;KACf,CAAC,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a project path once at the authority boundary.
|
|
3
|
+
*
|
|
4
|
+
* Every operation admitted by a trust decision must keep this exact result.
|
|
5
|
+
* Re-resolving a lexical symlink later would let it be redirected after the
|
|
6
|
+
* operator approved a different directory.
|
|
7
|
+
*/
|
|
8
|
+
export declare function canonicalProjectPath(dir: string): string;
|
|
9
|
+
//# sourceMappingURL=canonical-project.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical-project.d.ts","sourceRoot":"","sources":["../../src/permissions/canonical-project.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAExD"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { realpathSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Resolve a project path once at the authority boundary.
|
|
5
|
+
*
|
|
6
|
+
* Every operation admitted by a trust decision must keep this exact result.
|
|
7
|
+
* Re-resolving a lexical symlink later would let it be redirected after the
|
|
8
|
+
* operator approved a different directory.
|
|
9
|
+
*/
|
|
10
|
+
export function canonicalProjectPath(dir) {
|
|
11
|
+
return realpathSync(resolve(dir));
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=canonical-project.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical-project.js","sourceRoot":"","sources":["../../src/permissions/canonical-project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAW;IAC/C,OAAO,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;AAClC,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An operator's own expectations about their own permission table, checked.
|
|
3
|
+
*
|
|
4
|
+
* ## Why config needs tests at all
|
|
5
|
+
*
|
|
6
|
+
* A `[permissions]` entry is a glob compiled to a regular expression and
|
|
7
|
+
* evaluated against a subject the operator never sees. Every part of that
|
|
8
|
+
* pipeline has been wrong at least once in this repository, and each time the
|
|
9
|
+
* failure was silent and permissive: a rule that read like a prohibition and
|
|
10
|
+
* decided nothing, an `allow` whose match began wherever the text did, a glob
|
|
11
|
+
* whose trailing star reached past the end of a command. In every case the
|
|
12
|
+
* config LOOKED right, and nothing the operator could run would have told them
|
|
13
|
+
* otherwise.
|
|
14
|
+
*
|
|
15
|
+
* A test case attached to the policy turns that into a startup failure. It is
|
|
16
|
+
* the same argument as `a-falsifiable-comment-is-a-test`, applied to
|
|
17
|
+
* configuration: a claim nobody can check is a claim nobody should believe.
|
|
18
|
+
*
|
|
19
|
+
* ## What a check is
|
|
20
|
+
*
|
|
21
|
+
* A tool, an input, and the decision the operator believes their table
|
|
22
|
+
* produces:
|
|
23
|
+
*
|
|
24
|
+
* "permissionChecks": [
|
|
25
|
+
* { "tool": "bash", "input": { "command": "git status --short" }, "expect": "allow" },
|
|
26
|
+
* { "tool": "bash", "input": { "command": "git status && rm -rf ~" }, "expect": "ask" }
|
|
27
|
+
* ]
|
|
28
|
+
*
|
|
29
|
+
* The second is the interesting one. It asserts a NEGATIVE — that the table
|
|
30
|
+
* does not stretch to cover a command the operator never named — and a negative
|
|
31
|
+
* is exactly what a table of globs cannot be read for.
|
|
32
|
+
*
|
|
33
|
+
* ## Why it runs the real gate
|
|
34
|
+
*
|
|
35
|
+
* Re-implementing the matching here would check this file against itself. The
|
|
36
|
+
* check compiles the operator's table to rules and hands them to the same
|
|
37
|
+
* `AuthorizationGate` a run uses, so what it verifies is the decision that will
|
|
38
|
+
* actually be taken.
|
|
39
|
+
*
|
|
40
|
+
* The floor is deliberately OFF while checking. `denyDangerousPatterns` would
|
|
41
|
+
* answer for the four catastrophic commands before any operator rule was
|
|
42
|
+
* consulted, so a check written about the table would silently be answered by
|
|
43
|
+
* something else — and would keep passing after the rule it was written for was
|
|
44
|
+
* deleted.
|
|
45
|
+
*/
|
|
46
|
+
import { type AuthorizationRule } from '@namzu/sdk';
|
|
47
|
+
import type { CompileDiagnostic, PermissionEffect } from './rules.js';
|
|
48
|
+
/** One operator-authored expectation about the permission table. */
|
|
49
|
+
export interface PermissionCheck {
|
|
50
|
+
readonly tool: string;
|
|
51
|
+
readonly input: Readonly<Record<string, unknown>>;
|
|
52
|
+
readonly expect: PermissionEffect;
|
|
53
|
+
}
|
|
54
|
+
export type PermissionChecksConfig = readonly PermissionCheck[];
|
|
55
|
+
/**
|
|
56
|
+
* Evaluate every check against the compiled table.
|
|
57
|
+
*
|
|
58
|
+
* Returns a diagnostic per failure rather than throwing, for the reason the
|
|
59
|
+
* compiler already collects rather than throws: one wrong expectation should
|
|
60
|
+
* cost that line and not the other nine. But a failure must be SAID — a check
|
|
61
|
+
* that silently passes is worse than no check, because it is a reason to
|
|
62
|
+
* believe.
|
|
63
|
+
*/
|
|
64
|
+
export declare function verifyPermissionChecks(rules: readonly AuthorizationRule[], checks: PermissionChecksConfig | undefined): CompileDiagnostic[];
|
|
65
|
+
//# sourceMappingURL=checks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checks.d.ts","sourceRoot":"","sources":["../../src/permissions/checks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,OAAO,EAAqB,KAAK,iBAAiB,EAAe,MAAM,YAAY,CAAA;AAEnF,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAGrE,oEAAoE;AACpE,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IACjD,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAA;CACjC;AAED,MAAM,MAAM,sBAAsB,GAAG,SAAS,eAAe,EAAE,CAAA;AAa/D;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACrC,KAAK,EAAE,SAAS,iBAAiB,EAAE,EACnC,MAAM,EAAE,sBAAsB,GAAG,SAAS,GACxC,iBAAiB,EAAE,CAwDrB"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An operator's own expectations about their own permission table, checked.
|
|
3
|
+
*
|
|
4
|
+
* ## Why config needs tests at all
|
|
5
|
+
*
|
|
6
|
+
* A `[permissions]` entry is a glob compiled to a regular expression and
|
|
7
|
+
* evaluated against a subject the operator never sees. Every part of that
|
|
8
|
+
* pipeline has been wrong at least once in this repository, and each time the
|
|
9
|
+
* failure was silent and permissive: a rule that read like a prohibition and
|
|
10
|
+
* decided nothing, an `allow` whose match began wherever the text did, a glob
|
|
11
|
+
* whose trailing star reached past the end of a command. In every case the
|
|
12
|
+
* config LOOKED right, and nothing the operator could run would have told them
|
|
13
|
+
* otherwise.
|
|
14
|
+
*
|
|
15
|
+
* A test case attached to the policy turns that into a startup failure. It is
|
|
16
|
+
* the same argument as `a-falsifiable-comment-is-a-test`, applied to
|
|
17
|
+
* configuration: a claim nobody can check is a claim nobody should believe.
|
|
18
|
+
*
|
|
19
|
+
* ## What a check is
|
|
20
|
+
*
|
|
21
|
+
* A tool, an input, and the decision the operator believes their table
|
|
22
|
+
* produces:
|
|
23
|
+
*
|
|
24
|
+
* "permissionChecks": [
|
|
25
|
+
* { "tool": "bash", "input": { "command": "git status --short" }, "expect": "allow" },
|
|
26
|
+
* { "tool": "bash", "input": { "command": "git status && rm -rf ~" }, "expect": "ask" }
|
|
27
|
+
* ]
|
|
28
|
+
*
|
|
29
|
+
* The second is the interesting one. It asserts a NEGATIVE — that the table
|
|
30
|
+
* does not stretch to cover a command the operator never named — and a negative
|
|
31
|
+
* is exactly what a table of globs cannot be read for.
|
|
32
|
+
*
|
|
33
|
+
* ## Why it runs the real gate
|
|
34
|
+
*
|
|
35
|
+
* Re-implementing the matching here would check this file against itself. The
|
|
36
|
+
* check compiles the operator's table to rules and hands them to the same
|
|
37
|
+
* `AuthorizationGate` a run uses, so what it verifies is the decision that will
|
|
38
|
+
* actually be taken.
|
|
39
|
+
*
|
|
40
|
+
* The floor is deliberately OFF while checking. `denyDangerousPatterns` would
|
|
41
|
+
* answer for the four catastrophic commands before any operator rule was
|
|
42
|
+
* consulted, so a check written about the table would silently be answered by
|
|
43
|
+
* something else — and would keep passing after the rule it was written for was
|
|
44
|
+
* deleted.
|
|
45
|
+
*/
|
|
46
|
+
import { AuthorizationGate, NOOP_LOGGER } from '@namzu/sdk';
|
|
47
|
+
import { isPermissionEffect } from './rules.js';
|
|
48
|
+
/**
|
|
49
|
+
* The gate's vocabulary against the operator's.
|
|
50
|
+
*
|
|
51
|
+
* `review` is what the gate returns when nothing decided, and `ask` is what the
|
|
52
|
+
* operator writes for it. Two names for one outcome is a translation, not a
|
|
53
|
+
* mapping to be got wrong in one direction.
|
|
54
|
+
*/
|
|
55
|
+
function asEffect(decision) {
|
|
56
|
+
return decision === 'review' ? 'ask' : decision;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Evaluate every check against the compiled table.
|
|
60
|
+
*
|
|
61
|
+
* Returns a diagnostic per failure rather than throwing, for the reason the
|
|
62
|
+
* compiler already collects rather than throws: one wrong expectation should
|
|
63
|
+
* cost that line and not the other nine. But a failure must be SAID — a check
|
|
64
|
+
* that silently passes is worse than no check, because it is a reason to
|
|
65
|
+
* believe.
|
|
66
|
+
*/
|
|
67
|
+
export function verifyPermissionChecks(rules, checks) {
|
|
68
|
+
if (!checks || checks.length === 0)
|
|
69
|
+
return [];
|
|
70
|
+
const diagnostics = [];
|
|
71
|
+
const gate = new AuthorizationGate({
|
|
72
|
+
enabled: true,
|
|
73
|
+
rules: [...rules],
|
|
74
|
+
allowReadOnlyTools: false,
|
|
75
|
+
// Off on purpose; see the note at the top of this file.
|
|
76
|
+
denyDangerousPatterns: false,
|
|
77
|
+
logDecisions: false,
|
|
78
|
+
}, NOOP_LOGGER);
|
|
79
|
+
for (const [index, check] of checks.entries()) {
|
|
80
|
+
const where = `permissionChecks[${index}]`;
|
|
81
|
+
if (typeof check?.tool !== 'string' || check.tool === '') {
|
|
82
|
+
diagnostics.push({ tool: where, message: 'expected a "tool" naming the tool to evaluate' });
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (typeof check.input !== 'object' || check.input === null || Array.isArray(check.input)) {
|
|
86
|
+
diagnostics.push({
|
|
87
|
+
tool: where,
|
|
88
|
+
message: `expected an "input" object for ${check.tool}; got ${describe(check.input)}`,
|
|
89
|
+
});
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (!isPermissionEffect(check.expect)) {
|
|
93
|
+
diagnostics.push({
|
|
94
|
+
tool: where,
|
|
95
|
+
message: `expected "expect" to be "allow", "ask" or "deny"; got ${JSON.stringify(check.expect)}`,
|
|
96
|
+
});
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const verdict = gate.evaluate({
|
|
100
|
+
toolName: check.tool,
|
|
101
|
+
toolInput: check.input,
|
|
102
|
+
toolDef: undefined,
|
|
103
|
+
});
|
|
104
|
+
const actual = asEffect(verdict.decision);
|
|
105
|
+
if (actual === check.expect)
|
|
106
|
+
continue;
|
|
107
|
+
// The reason is quoted because it names the rule that decided, which is
|
|
108
|
+
// the difference between "your config is wrong" and a message someone
|
|
109
|
+
// can act on without re-deriving the whole table.
|
|
110
|
+
diagnostics.push({
|
|
111
|
+
tool: where,
|
|
112
|
+
message: `${check.tool} ${JSON.stringify(check.input)} is "${actual}", not the expected "${check.expect}" — ${verdict.reason}`,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return diagnostics;
|
|
116
|
+
}
|
|
117
|
+
function describe(value) {
|
|
118
|
+
if (value === null)
|
|
119
|
+
return 'null';
|
|
120
|
+
return Array.isArray(value) ? 'an array' : typeof value;
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=checks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checks.js","sourceRoot":"","sources":["../../src/permissions/checks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,OAAO,EAAE,iBAAiB,EAA0B,WAAW,EAAE,MAAM,YAAY,CAAA;AAGnF,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAW/C;;;;;;GAMG;AACH,SAAS,QAAQ,CAAC,QAAqC;IACtD,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAA;AAChD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CACrC,KAAmC,EACnC,MAA0C;IAE1C,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IAE7C,MAAM,WAAW,GAAwB,EAAE,CAAA;IAC3C,MAAM,IAAI,GAAG,IAAI,iBAAiB,CACjC;QACC,OAAO,EAAE,IAAI;QACb,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC;QACjB,kBAAkB,EAAE,KAAK;QACzB,wDAAwD;QACxD,qBAAqB,EAAE,KAAK;QAC5B,YAAY,EAAE,KAAK;KACnB,EACD,WAAW,CACX,CAAA;IAED,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;QAC/C,MAAM,KAAK,GAAG,oBAAoB,KAAK,GAAG,CAAA;QAE1C,IAAI,OAAO,KAAK,EAAE,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,EAAE,EAAE,CAAC;YAC1D,WAAW,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,+CAA+C,EAAE,CAAC,CAAA;YAC3F,SAAQ;QACT,CAAC;QACD,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3F,WAAW,CAAC,IAAI,CAAC;gBAChB,IAAI,EAAE,KAAK;gBACX,OAAO,EAAE,kCAAkC,KAAK,CAAC,IAAI,SAAS,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;aACrF,CAAC,CAAA;YACF,SAAQ;QACT,CAAC;QACD,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YACvC,WAAW,CAAC,IAAI,CAAC;gBAChB,IAAI,EAAE,KAAK;gBACX,OAAO,EAAE,yDAAyD,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE;aAChG,CAAC,CAAA;YACF,SAAQ;QACT,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC;YAC7B,QAAQ,EAAE,KAAK,CAAC,IAAI;YACpB,SAAS,EAAE,KAAK,CAAC,KAAK;YACtB,OAAO,EAAE,SAAS;SAClB,CAAC,CAAA;QACF,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;QACzC,IAAI,MAAM,KAAK,KAAK,CAAC,MAAM;YAAE,SAAQ;QAErC,wEAAwE;QACxE,sEAAsE;QACtE,kDAAkD;QAClD,WAAW,CAAC,IAAI,CAAC;YAChB,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,GAAG,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,MAAM,wBAAwB,KAAK,CAAC,MAAM,OAAO,OAAO,CAAC,MAAM,EAAE;SAC9H,CAAC,CAAA;IACH,CAAC;IAED,OAAO,WAAW,CAAA;AACnB,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC/B,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,MAAM,CAAA;IACjC,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,KAAK,CAAA;AACxD,CAAC"}
|
|
@@ -44,11 +44,15 @@
|
|
|
44
44
|
* an answer, which is the failure this package keeps finding rather than a
|
|
45
45
|
* milder version of it.
|
|
46
46
|
*/
|
|
47
|
-
export
|
|
48
|
-
readonly allowed:
|
|
49
|
-
/**
|
|
50
|
-
readonly
|
|
51
|
-
}
|
|
47
|
+
export type TrustDecision = {
|
|
48
|
+
readonly allowed: true;
|
|
49
|
+
/** Canonical target captured by the decision; use it for every later operation. */
|
|
50
|
+
readonly cwd: string;
|
|
51
|
+
} | {
|
|
52
|
+
readonly allowed: false;
|
|
53
|
+
/** Names the folder and both ways forward. */
|
|
54
|
+
readonly message: string;
|
|
55
|
+
};
|
|
52
56
|
export interface TrustCheck {
|
|
53
57
|
readonly cwd: string;
|
|
54
58
|
/** `--trust` was passed: the operator accepts this folder for this run. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headless-trust.d.ts","sourceRoot":"","sources":["../../src/permissions/headless-trust.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;
|
|
1
|
+
{"version":3,"file":"headless-trust.d.ts","sourceRoot":"","sources":["../../src/permissions/headless-trust.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAKH,MAAM,MAAM,aAAa,GACtB;IACA,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAA;IACtB,mFAAmF;IACnF,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACnB,GACD;IACA,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAA;IACvB,8CAA8C;IAC9C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACvB,CAAA;AAEJ,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,2EAA2E;IAC3E,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;IAC3B,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAA;CAC3C;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,aAAa,CAiCpE"}
|
|
@@ -45,20 +45,34 @@
|
|
|
45
45
|
* milder version of it.
|
|
46
46
|
*/
|
|
47
47
|
import { isTrusted } from '../integrations/trust/store.js';
|
|
48
|
+
import { canonicalProjectPath } from './canonical-project.js';
|
|
48
49
|
export function decideHeadlessTrust(check) {
|
|
50
|
+
// Capture the real target exactly once. The permission and every operation
|
|
51
|
+
// it admits must refer to the same directory entry even if the lexical
|
|
52
|
+
// `--cwd` was a symlink and somebody repoints it immediately afterward.
|
|
53
|
+
let cwd;
|
|
54
|
+
try {
|
|
55
|
+
cwd = canonicalProjectPath(check.cwd);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return {
|
|
59
|
+
allowed: false,
|
|
60
|
+
message: `refusing to run because the working directory changed or became unavailable while trust was being checked: ${check.cwd}`,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
49
63
|
// `--trust` is per-run and is deliberately NOT written to the trust file.
|
|
50
64
|
// One reflexive use must not change the machine's state forever; the TUI
|
|
51
65
|
// stays the only path that records durable trust, because that is the one
|
|
52
66
|
// where a human is actually looking at a prompt.
|
|
53
67
|
if (check.trustFlag)
|
|
54
|
-
return { allowed: true };
|
|
68
|
+
return { allowed: true, cwd };
|
|
55
69
|
const isDirTrusted = check.trusted ?? ((dir) => isTrusted(dir));
|
|
56
|
-
if (isDirTrusted(
|
|
57
|
-
return { allowed: true };
|
|
70
|
+
if (isDirTrusted(cwd))
|
|
71
|
+
return { allowed: true, cwd };
|
|
58
72
|
return {
|
|
59
73
|
allowed: false,
|
|
60
74
|
message: [
|
|
61
|
-
`refusing to run in a folder nobody has trusted: ${
|
|
75
|
+
`refusing to run in a folder nobody has trusted: ${cwd}`,
|
|
62
76
|
'',
|
|
63
77
|
"namzu reads this folder's files, runs commands in it and executes its",
|
|
64
78
|
'code, and a headless run approves those tools without asking because',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headless-trust.js","sourceRoot":"","sources":["../../src/permissions/headless-trust.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAA;
|
|
1
|
+
{"version":3,"file":"headless-trust.js","sourceRoot":"","sources":["../../src/permissions/headless-trust.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAA;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAsB7D,MAAM,UAAU,mBAAmB,CAAC,KAAiB;IACpD,2EAA2E;IAC3E,uEAAuE;IACvE,wEAAwE;IACxE,IAAI,GAAW,CAAA;IACf,IAAI,CAAC;QACJ,GAAG,GAAG,oBAAoB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IACtC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO;YACN,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,8GAA8G,KAAK,CAAC,GAAG,EAAE;SAClI,CAAA;IACF,CAAC;IACD,0EAA0E;IAC1E,yEAAyE;IACzE,0EAA0E;IAC1E,iDAAiD;IACjD,IAAI,KAAK,CAAC,SAAS;QAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,CAAA;IAClD,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC,GAAW,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAA;IACvE,IAAI,YAAY,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,CAAA;IACpD,OAAO;QACN,OAAO,EAAE,KAAK;QACd,OAAO,EAAE;YACR,mDAAmD,GAAG,EAAE;YACxD,EAAE;YACF,uEAAuE;YACvE,sEAAsE;YACtE,yBAAyB;YACzB,EAAE;YACF,uEAAuE;YACvE,8DAA8D;SAC9D,CAAC,IAAI,CAAC,IAAI,CAAC;KACZ,CAAA;AACF,CAAC"}
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
* The whole surface is pure: config in, rules out. The gate stays the only
|
|
11
11
|
* thing that decides anything, which is what keeps this testable without a run.
|
|
12
12
|
*/
|
|
13
|
-
import type
|
|
13
|
+
import { type AuthorizationRule } from '@namzu/sdk';
|
|
14
|
+
import { type PermissionChecksConfig } from './checks.js';
|
|
14
15
|
/** What the operator wants to happen when a rule matches. */
|
|
15
16
|
export type PermissionEffect = 'allow' | 'ask' | 'deny';
|
|
16
17
|
export declare function isPermissionEffect(value: unknown): value is PermissionEffect;
|
|
@@ -95,8 +96,8 @@ export declare function patternToRegExpSource(pattern: string): string;
|
|
|
95
96
|
* own leading `.*` sits after the quote. `git status*` is anchored, and
|
|
96
97
|
* `*git status*` is not.
|
|
97
98
|
*
|
|
98
|
-
* **Two loosenesses remain, and
|
|
99
|
-
*
|
|
99
|
+
* **Two loosenesses remain here, and both come from matching a glob against a
|
|
100
|
+
* SERIALISED OBJECT rather than against a value.**
|
|
100
101
|
*
|
|
101
102
|
* 1. A pattern can match the start of ANY argument's value, not only the one
|
|
102
103
|
* the operator had in mind.
|
|
@@ -108,9 +109,17 @@ export declare function patternToRegExpSource(pattern: string): string;
|
|
|
108
109
|
* the serialised object and would match nothing.
|
|
109
110
|
*
|
|
110
111
|
* The kernel's `argument_pattern` removes both, because its subject is the
|
|
111
|
-
* argument's own value and it needs neither escape.
|
|
112
|
-
*
|
|
113
|
-
*
|
|
112
|
+
* argument's own value and it needs neither escape. This comment used to say
|
|
113
|
+
* reaching it needed an operator-facing way to NAME the argument, and that was
|
|
114
|
+
* the wrong place to look for the name: the operator does not know it and
|
|
115
|
+
* should not have to, while the TOOL always does. A tool that declares
|
|
116
|
+
* `commandArgument` is compiled through `argument_pattern` instead, which is
|
|
117
|
+
* why `bash` no longer arrives here at all.
|
|
118
|
+
*
|
|
119
|
+
* What is left for this function is every tool that declares nothing — MCP
|
|
120
|
+
* servers, host tools, `edit` and `read` — where a glob against the serialised
|
|
121
|
+
* input remains the only subject available, and where loosening 2 is bounded
|
|
122
|
+
* by there being no command line to chain onto.
|
|
114
123
|
*/
|
|
115
124
|
export declare function toolScopedPattern(tool: string, pattern: string, decision?: 'allow' | 'deny'): string;
|
|
116
125
|
/** Whether `input` satisfies `pattern`, with the matching above. */
|
|
@@ -139,5 +148,5 @@ export declare function bySpecificity(a: string, b: string): number;
|
|
|
139
148
|
* A tool nobody wrote a rule for is therefore asked about, and there is no way
|
|
140
149
|
* to spell "allow everything by omission" — the only way to widen is to say so.
|
|
141
150
|
*/
|
|
142
|
-
export declare function compilePermissions(config: PermissionsConfig | undefined): CompiledPermissions;
|
|
151
|
+
export declare function compilePermissions(config: PermissionsConfig | undefined, checks?: PermissionChecksConfig): CompiledPermissions;
|
|
143
152
|
//# sourceMappingURL=rules.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/permissions/rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/permissions/rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,KAAK,iBAAiB,EAA2B,MAAM,YAAY,CAAA;AAE5E,OAAO,EAAE,KAAK,sBAAsB,EAA0B,MAAM,aAAa,CAAA;AAEjF,6DAA6D;AAC7D,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAA;AAIvD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,gBAAgB,CAE5E;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG,gBAAgB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAA;AAE1F,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAA;AAExE,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,KAAK,EAAE,SAAS,iBAAiB,EAAE,CAAA;IAC5C;;;;;;;OAOG;IACH,QAAQ,CAAC,WAAW,EAAE,SAAS,iBAAiB,EAAE,CAAA;CAClD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAQ7D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,wBAAgB,iBAAiB,CAChC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,OAAO,GAAG,MAAe,GACjC,MAAM,CASR;AA+CD,oEAAoE;AACpE,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAKtE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAU1D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CACjC,MAAM,EAAE,iBAAiB,GAAG,SAAS,EACrC,MAAM,CAAC,EAAE,sBAAsB,GAC7B,mBAAmB,CAqErB"}
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
* The whole surface is pure: config in, rules out. The gate stays the only
|
|
11
11
|
* thing that decides anything, which is what keeps this testable without a run.
|
|
12
12
|
*/
|
|
13
|
+
import { builtinCommandArguments } from '@namzu/sdk';
|
|
14
|
+
import { verifyPermissionChecks } from './checks.js';
|
|
13
15
|
const EFFECTS = new Set(['allow', 'ask', 'deny']);
|
|
14
16
|
export function isPermissionEffect(value) {
|
|
15
17
|
return typeof value === 'string' && EFFECTS.has(value);
|
|
@@ -76,8 +78,8 @@ export function patternToRegExpSource(pattern) {
|
|
|
76
78
|
* own leading `.*` sits after the quote. `git status*` is anchored, and
|
|
77
79
|
* `*git status*` is not.
|
|
78
80
|
*
|
|
79
|
-
* **Two loosenesses remain, and
|
|
80
|
-
*
|
|
81
|
+
* **Two loosenesses remain here, and both come from matching a glob against a
|
|
82
|
+
* SERIALISED OBJECT rather than against a value.**
|
|
81
83
|
*
|
|
82
84
|
* 1. A pattern can match the start of ANY argument's value, not only the one
|
|
83
85
|
* the operator had in mind.
|
|
@@ -89,9 +91,17 @@ export function patternToRegExpSource(pattern) {
|
|
|
89
91
|
* the serialised object and would match nothing.
|
|
90
92
|
*
|
|
91
93
|
* The kernel's `argument_pattern` removes both, because its subject is the
|
|
92
|
-
* argument's own value and it needs neither escape.
|
|
93
|
-
*
|
|
94
|
-
*
|
|
94
|
+
* argument's own value and it needs neither escape. This comment used to say
|
|
95
|
+
* reaching it needed an operator-facing way to NAME the argument, and that was
|
|
96
|
+
* the wrong place to look for the name: the operator does not know it and
|
|
97
|
+
* should not have to, while the TOOL always does. A tool that declares
|
|
98
|
+
* `commandArgument` is compiled through `argument_pattern` instead, which is
|
|
99
|
+
* why `bash` no longer arrives here at all.
|
|
100
|
+
*
|
|
101
|
+
* What is left for this function is every tool that declares nothing — MCP
|
|
102
|
+
* servers, host tools, `edit` and `read` — where a glob against the serialised
|
|
103
|
+
* input remains the only subject available, and where loosening 2 is bounded
|
|
104
|
+
* by there being no command line to chain onto.
|
|
95
105
|
*/
|
|
96
106
|
export function toolScopedPattern(tool, pattern, decision = 'deny') {
|
|
97
107
|
const toolPart = escapeRegExp(tool);
|
|
@@ -103,6 +113,41 @@ export function toolScopedPattern(tool, pattern, decision = 'deny') {
|
|
|
103
113
|
const anchorToValue = decision === 'allow' && pattern.replaceAll('*', '') !== '';
|
|
104
114
|
return anchorToValue ? `^${toolPart} .*"${argPart}.*$` : `^${toolPart} .*${argPart}.*$`;
|
|
105
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* Compile one pattern for a tool that declares a command argument.
|
|
118
|
+
*
|
|
119
|
+
* This carries the asymmetry `toolScopedPattern` established, because the
|
|
120
|
+
* reasons for it did not change when the subject did. An `allow` anchors: it is
|
|
121
|
+
* a claim about a command, and a claim that matches a prefix is a claim about
|
|
122
|
+
* something else. A `deny` stays loose: an operator who denied `rm -rf*` means
|
|
123
|
+
* it wherever it appears, and `sudo rm -rf /` is the case that proves it —
|
|
124
|
+
* anchoring the deny would stop it matching, and a deny that stops matching
|
|
125
|
+
* fails open.
|
|
126
|
+
*
|
|
127
|
+
* What the subject gained is the part that could not be done before. The gate
|
|
128
|
+
* reads this argument as the COMMANDS the line runs, so the anchored `allow`
|
|
129
|
+
* is anchored per command: `git status*` no longer approves
|
|
130
|
+
* `git status && rm -rf ~`, and the loose `deny` now also sees a command that
|
|
131
|
+
* rides behind a separator.
|
|
132
|
+
*/
|
|
133
|
+
function commandRule(tool, argument, pattern, decision) {
|
|
134
|
+
// All wildcards means "every call to this tool", and that has to keep
|
|
135
|
+
// meaning it. As a pattern it would stop being true the moment the tool is
|
|
136
|
+
// called without that argument, so it is emitted as the rule that says it.
|
|
137
|
+
if (pattern.replaceAll('*', '') === '') {
|
|
138
|
+
return decision === 'allow'
|
|
139
|
+
? { type: 'allow_by_name', toolNames: [tool] }
|
|
140
|
+
: { type: 'deny_by_name', toolNames: [tool] };
|
|
141
|
+
}
|
|
142
|
+
const anchored = patternToRegExpSource(pattern);
|
|
143
|
+
return {
|
|
144
|
+
type: 'argument_pattern',
|
|
145
|
+
toolNames: [tool],
|
|
146
|
+
argument,
|
|
147
|
+
pattern: decision === 'allow' ? anchored : anchored.slice(1, -1),
|
|
148
|
+
decision,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
106
151
|
function escapeRegExp(value) {
|
|
107
152
|
return value.replace(/[.*+^${}()|[\]\\?]/g, '\\$&');
|
|
108
153
|
}
|
|
@@ -150,11 +195,18 @@ export function bySpecificity(a, b) {
|
|
|
150
195
|
* A tool nobody wrote a rule for is therefore asked about, and there is no way
|
|
151
196
|
* to spell "allow everything by omission" — the only way to widen is to say so.
|
|
152
197
|
*/
|
|
153
|
-
export function compilePermissions(config) {
|
|
198
|
+
export function compilePermissions(config, checks) {
|
|
154
199
|
const rules = [];
|
|
155
200
|
const diagnostics = [];
|
|
201
|
+
// Checks still run against an empty table. "This command is asked about"
|
|
202
|
+
// is a claim worth holding when there is no config at all, and returning
|
|
203
|
+
// early would make the check silently pass by never running.
|
|
156
204
|
if (!config)
|
|
157
|
-
return { rules, diagnostics };
|
|
205
|
+
return { rules, diagnostics: verifyPermissionChecks(rules, checks) };
|
|
206
|
+
// Asked once. The map is derived from the builtin tools rather than listed
|
|
207
|
+
// here, so a tool that starts or stops taking a command line changes this
|
|
208
|
+
// compiler by changing itself.
|
|
209
|
+
const commandArguments = builtinCommandArguments();
|
|
158
210
|
for (const [tool, permission] of Object.entries(config)) {
|
|
159
211
|
if (isPermissionEffect(permission)) {
|
|
160
212
|
if (permission === 'allow')
|
|
@@ -193,14 +245,27 @@ export function compilePermissions(config) {
|
|
|
193
245
|
});
|
|
194
246
|
continue;
|
|
195
247
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
248
|
+
// A tool that declares which of its arguments holds a command line
|
|
249
|
+
// gets the rule that can read one. `custom_pattern` stays the
|
|
250
|
+
// fallback for everything else, and the two loosenesses documented
|
|
251
|
+
// on `toolScopedPattern` are exactly what this branch removes.
|
|
252
|
+
const argument = commandArguments.get(tool);
|
|
253
|
+
if (argument === undefined) {
|
|
254
|
+
rules.push({
|
|
255
|
+
type: 'custom_pattern',
|
|
256
|
+
pattern: toolScopedPattern(tool, pattern, effect),
|
|
257
|
+
target: 'both',
|
|
258
|
+
decision: effect,
|
|
259
|
+
});
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
rules.push(commandRule(tool, argument, pattern, effect));
|
|
202
263
|
}
|
|
203
264
|
}
|
|
265
|
+
// After compiling, never during: a check is a claim about the FINISHED
|
|
266
|
+
// table, and evaluating one against a half-built rule list would answer
|
|
267
|
+
// with whatever order the config happened to be written in.
|
|
268
|
+
diagnostics.push(...verifyPermissionChecks(rules, checks));
|
|
204
269
|
return { rules, diagnostics };
|
|
205
270
|
}
|
|
206
271
|
//# sourceMappingURL=rules.js.map
|