sigild 0.0.1 → 0.0.2
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 +107 -34
- package/THREAT_MODEL.md +32 -15
- package/dist/src/bin/sigil-hook-post.d.ts +3 -0
- package/dist/src/bin/sigil-hook-post.d.ts.map +1 -0
- package/dist/src/bin/sigil-hook-post.js +15 -0
- package/dist/src/bin/sigil-hook-post.js.map +1 -0
- package/dist/src/bin/sigil-hook-pre.d.ts +3 -0
- package/dist/src/bin/sigil-hook-pre.d.ts.map +1 -0
- package/dist/src/bin/sigil-hook-pre.js +18 -0
- package/dist/src/bin/sigil-hook-pre.js.map +1 -0
- package/dist/src/bin/sigil-mcp.d.ts +3 -0
- package/dist/src/bin/sigil-mcp.d.ts.map +1 -0
- package/dist/src/bin/sigil-mcp.js +87 -0
- package/dist/src/bin/sigil-mcp.js.map +1 -0
- package/dist/src/bin/sigil.d.ts +3 -0
- package/dist/src/bin/sigil.d.ts.map +1 -0
- package/dist/src/bin/sigil.js +9 -0
- package/dist/src/bin/sigil.js.map +1 -0
- package/dist/src/cli/args.d.ts +26 -0
- package/dist/src/cli/args.d.ts.map +1 -0
- package/dist/src/cli/args.js +36 -0
- package/dist/src/cli/args.js.map +1 -0
- package/dist/src/cli/index.d.ts +7 -0
- package/dist/src/cli/index.d.ts.map +1 -0
- package/dist/src/cli/index.js +7 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/cli/main.d.ts +26 -0
- package/dist/src/cli/main.d.ts.map +1 -0
- package/dist/src/cli/main.js +152 -0
- package/dist/src/cli/main.js.map +1 -0
- package/dist/src/cli/paths.d.ts +17 -0
- package/dist/src/cli/paths.d.ts.map +1 -0
- package/dist/src/cli/paths.js +12 -0
- package/dist/src/cli/paths.js.map +1 -0
- package/dist/src/cli/portal.d.ts +50 -0
- package/dist/src/cli/portal.d.ts.map +1 -0
- package/dist/src/cli/portal.js +93 -0
- package/dist/src/cli/portal.js.map +1 -0
- package/dist/src/cli/status.d.ts +28 -0
- package/dist/src/cli/status.d.ts.map +1 -0
- package/dist/src/cli/status.js +59 -0
- package/dist/src/cli/status.js.map +1 -0
- package/dist/src/cli/unlock.d.ts +36 -0
- package/dist/src/cli/unlock.d.ts.map +1 -0
- package/dist/src/cli/unlock.js +77 -0
- package/dist/src/cli/unlock.js.map +1 -0
- package/dist/src/control/client.d.ts +26 -0
- package/dist/src/control/client.d.ts.map +1 -0
- package/dist/src/control/client.js +76 -0
- package/dist/src/control/client.js.map +1 -0
- package/dist/src/control/index.d.ts +4 -0
- package/dist/src/control/index.d.ts.map +1 -0
- package/dist/src/control/index.js +4 -0
- package/dist/src/control/index.js.map +1 -0
- package/dist/src/control/protocol.d.ts +54 -0
- package/dist/src/control/protocol.d.ts.map +1 -0
- package/dist/src/control/protocol.js +60 -0
- package/dist/src/control/protocol.js.map +1 -0
- package/dist/src/control/server.d.ts +52 -0
- package/dist/src/control/server.d.ts.map +1 -0
- package/dist/src/control/server.js +199 -0
- package/dist/src/control/server.js.map +1 -0
- package/dist/src/daemon/handles.d.ts +35 -6
- package/dist/src/daemon/handles.d.ts.map +1 -1
- package/dist/src/daemon/handles.js +83 -28
- package/dist/src/daemon/handles.js.map +1 -1
- package/dist/src/daemon/index.d.ts +2 -3
- package/dist/src/daemon/index.d.ts.map +1 -1
- package/dist/src/daemon/index.js +2 -3
- package/dist/src/daemon/index.js.map +1 -1
- package/dist/src/daemon/methods.d.ts +6 -0
- package/dist/src/daemon/methods.d.ts.map +1 -1
- package/dist/src/daemon/methods.js +13 -1
- package/dist/src/daemon/methods.js.map +1 -1
- package/dist/src/hooks/command-scanner.d.ts +5 -0
- package/dist/src/hooks/command-scanner.d.ts.map +1 -0
- package/dist/src/hooks/command-scanner.js +117 -0
- package/dist/src/hooks/command-scanner.js.map +1 -0
- package/dist/src/hooks/glob.d.ts +8 -0
- package/dist/src/hooks/glob.d.ts.map +1 -0
- package/dist/src/hooks/glob.js +98 -0
- package/dist/src/hooks/glob.js.map +1 -0
- package/dist/src/hooks/index.d.ts +9 -0
- package/dist/src/hooks/index.d.ts.map +1 -0
- package/dist/src/hooks/index.js +9 -0
- package/dist/src/hooks/index.js.map +1 -0
- package/dist/src/hooks/install.d.ts +29 -0
- package/dist/src/hooks/install.d.ts.map +1 -0
- package/dist/src/hooks/install.js +86 -0
- package/dist/src/hooks/install.js.map +1 -0
- package/dist/src/hooks/path-blocker.d.ts +29 -0
- package/dist/src/hooks/path-blocker.d.ts.map +1 -0
- package/dist/src/hooks/path-blocker.js +59 -0
- package/dist/src/hooks/path-blocker.js.map +1 -0
- package/dist/src/hooks/post-tool-use.d.ts +13 -0
- package/dist/src/hooks/post-tool-use.d.ts.map +1 -0
- package/dist/src/hooks/post-tool-use.js +45 -0
- package/dist/src/hooks/post-tool-use.js.map +1 -0
- package/dist/src/hooks/pre-tool-use.d.ts +8 -0
- package/dist/src/hooks/pre-tool-use.d.ts.map +1 -0
- package/dist/src/hooks/pre-tool-use.js +38 -0
- package/dist/src/hooks/pre-tool-use.js.map +1 -0
- package/dist/src/hooks/protocol.d.ts +41 -0
- package/dist/src/hooks/protocol.d.ts.map +1 -0
- package/dist/src/hooks/protocol.js +27 -0
- package/dist/src/hooks/protocol.js.map +1 -0
- package/dist/src/hooks/redactor.d.ts +19 -0
- package/dist/src/hooks/redactor.d.ts.map +1 -0
- package/dist/src/hooks/redactor.js +71 -0
- package/dist/src/hooks/redactor.js.map +1 -0
- package/dist/src/mcp/index.d.ts +4 -0
- package/dist/src/mcp/index.d.ts.map +1 -0
- package/dist/src/mcp/index.js +4 -0
- package/dist/src/mcp/index.js.map +1 -0
- package/dist/src/mcp/protocol.d.ts +98 -0
- package/dist/src/mcp/protocol.d.ts.map +1 -0
- package/dist/src/mcp/protocol.js +79 -0
- package/dist/src/mcp/protocol.js.map +1 -0
- package/dist/src/mcp/server.d.ts +46 -0
- package/dist/src/mcp/server.d.ts.map +1 -0
- package/dist/src/mcp/server.js +108 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/dist/src/mcp/tools.d.ts +16 -0
- package/dist/src/mcp/tools.d.ts.map +1 -0
- package/dist/src/mcp/tools.js +117 -0
- package/dist/src/mcp/tools.js.map +1 -0
- package/package.json +8 -3
- package/dist/src/bin/sigild.d.ts +0 -3
- package/dist/src/bin/sigild.d.ts.map +0 -1
- package/dist/src/bin/sigild.js +0 -30
- package/dist/src/bin/sigild.js.map +0 -1
- package/dist/src/daemon/rpc.d.ts +0 -61
- package/dist/src/daemon/rpc.d.ts.map +0 -1
- package/dist/src/daemon/rpc.js +0 -76
- package/dist/src/daemon/rpc.js.map +0 -1
- package/dist/src/daemon/runtime.d.ts +0 -40
- package/dist/src/daemon/runtime.d.ts.map +0 -1
- package/dist/src/daemon/runtime.js +0 -61
- package/dist/src/daemon/runtime.js.map +0 -1
- package/dist/src/daemon/server.d.ts +0 -53
- package/dist/src/daemon/server.d.ts.map +0 -1
- package/dist/src/daemon/server.js +0 -103
- package/dist/src/daemon/server.js.map +0 -1
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join, resolve } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
const SIGIL_MCP_NAME = 'sigil';
|
|
5
|
+
const SIGIL_HOOK_MARKER = 'sigil-hook-';
|
|
6
|
+
export function settingsPath(opts) {
|
|
7
|
+
if (opts.scope === 'user') {
|
|
8
|
+
const home = opts.homeDir ?? homedir();
|
|
9
|
+
return join(home, '.claude', 'settings.json');
|
|
10
|
+
}
|
|
11
|
+
const root = opts.projectRoot ? resolve(opts.projectRoot) : process.cwd();
|
|
12
|
+
return join(root, '.claude', 'settings.json');
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Idempotently install sigil's MCP server registration + hook config.
|
|
16
|
+
* Existing settings (other MCP servers, other hook handlers) are preserved.
|
|
17
|
+
*/
|
|
18
|
+
export function installInto(opts) {
|
|
19
|
+
const path = settingsPath(opts);
|
|
20
|
+
const before = readSettings(path);
|
|
21
|
+
const after = { ...before };
|
|
22
|
+
const mcpCommand = opts.mcpCommand ?? 'sigil-mcp';
|
|
23
|
+
// 1. MCP server registration — overwrite our entry; leave others alone.
|
|
24
|
+
const existingServers = after.mcpServers ?? {};
|
|
25
|
+
after.mcpServers = {
|
|
26
|
+
...existingServers,
|
|
27
|
+
[SIGIL_MCP_NAME]: { command: mcpCommand },
|
|
28
|
+
};
|
|
29
|
+
// 2. Hooks — remove any prior sigil entries (identified by the
|
|
30
|
+
// sigil-hook- prefix in the command path) then re-add fresh ones.
|
|
31
|
+
const stripSigil = (matchers) => {
|
|
32
|
+
if (!matchers)
|
|
33
|
+
return [];
|
|
34
|
+
return matchers
|
|
35
|
+
.map((m) => ({
|
|
36
|
+
...m,
|
|
37
|
+
hooks: m.hooks.filter((h) => !h.command.includes(SIGIL_HOOK_MARKER)),
|
|
38
|
+
}))
|
|
39
|
+
.filter((m) => m.hooks.length > 0);
|
|
40
|
+
};
|
|
41
|
+
const hooks = { ...(after.hooks ?? {}) };
|
|
42
|
+
hooks.PreToolUse = [
|
|
43
|
+
...stripSigil(hooks.PreToolUse),
|
|
44
|
+
{
|
|
45
|
+
matcher: 'Read|Bash',
|
|
46
|
+
hooks: [{ type: 'command', command: 'sigil-hook-pre' }],
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
hooks.PostToolUse = [
|
|
50
|
+
...stripSigil(hooks.PostToolUse),
|
|
51
|
+
{
|
|
52
|
+
matcher: '.*',
|
|
53
|
+
hooks: [{ type: 'command', command: 'sigil-hook-post' }],
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
after.hooks = hooks;
|
|
57
|
+
// Bail early if no change.
|
|
58
|
+
const beforeJson = JSON.stringify(before);
|
|
59
|
+
const afterJson = JSON.stringify(after);
|
|
60
|
+
if (beforeJson === afterJson && existsSync(path)) {
|
|
61
|
+
return { settingsPath: path, changed: false };
|
|
62
|
+
}
|
|
63
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
64
|
+
writeFileSync(path, JSON.stringify(after, null, 2) + '\n', { mode: 0o644 });
|
|
65
|
+
return { settingsPath: path, changed: true };
|
|
66
|
+
}
|
|
67
|
+
function readSettings(path) {
|
|
68
|
+
if (!existsSync(path))
|
|
69
|
+
return {};
|
|
70
|
+
try {
|
|
71
|
+
statSync(path);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return {};
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const text = readFileSync(path, 'utf8');
|
|
78
|
+
if (text.trim() === '')
|
|
79
|
+
return {};
|
|
80
|
+
return JSON.parse(text);
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
throw new Error(`could not parse ${path}: ${err.message}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../../src/hooks/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in patterns we always block. These are not user-configurable; they
|
|
3
|
+
* represent paths where a key would conventionally live and that the agent
|
|
4
|
+
* should never read.
|
|
5
|
+
*/
|
|
6
|
+
export declare const DEFAULT_PATH_PATTERNS: readonly string[];
|
|
7
|
+
export interface BlockDecision {
|
|
8
|
+
blocked: boolean;
|
|
9
|
+
/** The pattern that matched, useful for surfacing in the error to the agent. */
|
|
10
|
+
matchedPattern?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface BlockerOpts {
|
|
13
|
+
/** Extra glob patterns to block, in addition to the built-ins. */
|
|
14
|
+
extraPatterns?: readonly string[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Decide whether to block a path read. Both the resolved absolute path AND
|
|
18
|
+
* the raw user-supplied path are checked, because attackers (or confused
|
|
19
|
+
* agents) may pass paths with `..` or symlinks that resolve to a blocked
|
|
20
|
+
* location while looking innocent.
|
|
21
|
+
*
|
|
22
|
+
* Note: this does NOT resolve symlinks itself. Symlink resolution is best
|
|
23
|
+
* left to the caller (who has filesystem access). For sigil's hook context,
|
|
24
|
+
* the agent already has filesystem access and would do the symlink read
|
|
25
|
+
* before our hook fires; the meaningful protection is preventing direct
|
|
26
|
+
* reads through obvious paths.
|
|
27
|
+
*/
|
|
28
|
+
export declare function isBlockedPath(path: string, opts?: BlockerOpts): BlockDecision;
|
|
29
|
+
//# sourceMappingURL=path-blocker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-blocker.d.ts","sourceRoot":"","sources":["../../../src/hooks/path-blocker.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAE,SAAS,MAAM,EAuBjD,CAAC;AAEH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,gFAAgF;IAChF,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,kEAAkE;IAClE,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,aAAa,CAejF"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { basename } from 'node:path';
|
|
2
|
+
import { globMatch, normalizePath } from './glob.js';
|
|
3
|
+
/**
|
|
4
|
+
* Built-in patterns we always block. These are not user-configurable; they
|
|
5
|
+
* represent paths where a key would conventionally live and that the agent
|
|
6
|
+
* should never read.
|
|
7
|
+
*/
|
|
8
|
+
export const DEFAULT_PATH_PATTERNS = Object.freeze([
|
|
9
|
+
// sigil's own state
|
|
10
|
+
'**/.sigil/**',
|
|
11
|
+
// common key file extensions
|
|
12
|
+
'**/*.pem',
|
|
13
|
+
'**/*.key',
|
|
14
|
+
'**/*.keystore',
|
|
15
|
+
'**/*.jks',
|
|
16
|
+
'**/*.p12',
|
|
17
|
+
// SSH private key conventions
|
|
18
|
+
'**/.ssh/id_*',
|
|
19
|
+
'**/.ssh/*_rsa',
|
|
20
|
+
'**/.ssh/*_ed25519',
|
|
21
|
+
'**/.ssh/*_ecdsa',
|
|
22
|
+
// env files (no extension, dotted prefix variants like .env.local)
|
|
23
|
+
'**/.env',
|
|
24
|
+
'**/.env.*',
|
|
25
|
+
// ethereum/geth keystore directories
|
|
26
|
+
'**/keystore',
|
|
27
|
+
'**/keystore/**',
|
|
28
|
+
// GPG / pass(1) storage
|
|
29
|
+
'**/.gnupg/**',
|
|
30
|
+
'**/.password-store/**',
|
|
31
|
+
]);
|
|
32
|
+
/**
|
|
33
|
+
* Decide whether to block a path read. Both the resolved absolute path AND
|
|
34
|
+
* the raw user-supplied path are checked, because attackers (or confused
|
|
35
|
+
* agents) may pass paths with `..` or symlinks that resolve to a blocked
|
|
36
|
+
* location while looking innocent.
|
|
37
|
+
*
|
|
38
|
+
* Note: this does NOT resolve symlinks itself. Symlink resolution is best
|
|
39
|
+
* left to the caller (who has filesystem access). For sigil's hook context,
|
|
40
|
+
* the agent already has filesystem access and would do the symlink read
|
|
41
|
+
* before our hook fires; the meaningful protection is preventing direct
|
|
42
|
+
* reads through obvious paths.
|
|
43
|
+
*/
|
|
44
|
+
export function isBlockedPath(path, opts = {}) {
|
|
45
|
+
const patterns = [...DEFAULT_PATH_PATTERNS, ...(opts.extraPatterns ?? [])];
|
|
46
|
+
const normalized = normalizePath(path);
|
|
47
|
+
const base = basename(normalized);
|
|
48
|
+
for (const pattern of patterns) {
|
|
49
|
+
if (globMatch(normalized, pattern)) {
|
|
50
|
+
return { blocked: true, matchedPattern: pattern };
|
|
51
|
+
}
|
|
52
|
+
// Also test the basename so a pattern like `**/.env` catches relative `.env`.
|
|
53
|
+
if (globMatch(base, pattern.replace(/^\*\*\//, ''))) {
|
|
54
|
+
return { blocked: true, matchedPattern: pattern };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return { blocked: false };
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=path-blocker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-blocker.js","sourceRoot":"","sources":["../../../src/hooks/path-blocker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAErD;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAsB,MAAM,CAAC,MAAM,CAAC;IACpE,oBAAoB;IACpB,cAAc;IACd,6BAA6B;IAC7B,UAAU;IACV,UAAU;IACV,eAAe;IACf,UAAU;IACV,UAAU;IACV,8BAA8B;IAC9B,cAAc;IACd,eAAe;IACf,mBAAmB;IACnB,iBAAiB;IACjB,mEAAmE;IACnE,SAAS;IACT,WAAW;IACX,qCAAqC;IACrC,aAAa;IACb,gBAAgB;IAChB,wBAAwB;IACxB,cAAc;IACd,uBAAuB;CACxB,CAAC,CAAC;AAaH;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,OAAoB,EAAE;IAChE,MAAM,QAAQ,GAAG,CAAC,GAAG,qBAAqB,EAAE,GAAG,CAAC,IAAI,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC,CAAC;IAC3E,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;IAElC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,SAAS,CAAC,UAAU,EAAE,OAAO,CAAC,EAAE,CAAC;YACnC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC;QACpD,CAAC;QACD,8EAA8E;QAC9E,IAAI,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;YACpD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC;QACpD,CAAC;IACH,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAC5B,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { HookEnvelope, PostToolModification } from './protocol.js';
|
|
2
|
+
/**
|
|
3
|
+
* Walk the tool_response field of a PostToolUse envelope, redacting every
|
|
4
|
+
* string we find. Returns the modification envelope to emit, or null if
|
|
5
|
+
* nothing changed.
|
|
6
|
+
*/
|
|
7
|
+
export declare function decidePostToolUse(env: HookEnvelope): PostToolModification | null;
|
|
8
|
+
/**
|
|
9
|
+
* Recursively descend into objects, arrays, and strings, applying redact()
|
|
10
|
+
* to every string we encounter. Accumulates redaction counts.
|
|
11
|
+
*/
|
|
12
|
+
export declare function walkAndRedact(value: unknown, totals: Map<string, number>): unknown;
|
|
13
|
+
//# sourceMappingURL=post-tool-use.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-tool-use.d.ts","sourceRoot":"","sources":["../../../src/hooks/post-tool-use.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAExE;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,YAAY,GAAG,oBAAoB,GAAG,IAAI,CAchF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAiBlF"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { redact } from './redactor.js';
|
|
2
|
+
/**
|
|
3
|
+
* Walk the tool_response field of a PostToolUse envelope, redacting every
|
|
4
|
+
* string we find. Returns the modification envelope to emit, or null if
|
|
5
|
+
* nothing changed.
|
|
6
|
+
*/
|
|
7
|
+
export function decidePostToolUse(env) {
|
|
8
|
+
const response = env.tool_response;
|
|
9
|
+
if (response === undefined)
|
|
10
|
+
return null;
|
|
11
|
+
const totals = new Map();
|
|
12
|
+
const redacted = walkAndRedact(response, totals);
|
|
13
|
+
if (totals.size === 0)
|
|
14
|
+
return null;
|
|
15
|
+
return {
|
|
16
|
+
hookSpecificOutput: {
|
|
17
|
+
hookEventName: 'PostToolUse',
|
|
18
|
+
updatedToolResponse: redacted,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Recursively descend into objects, arrays, and strings, applying redact()
|
|
24
|
+
* to every string we encounter. Accumulates redaction counts.
|
|
25
|
+
*/
|
|
26
|
+
export function walkAndRedact(value, totals) {
|
|
27
|
+
if (typeof value === 'string') {
|
|
28
|
+
const r = redact(value);
|
|
29
|
+
for (const s of r.redactions)
|
|
30
|
+
totals.set(s.reason, (totals.get(s.reason) ?? 0) + s.count);
|
|
31
|
+
return r.text;
|
|
32
|
+
}
|
|
33
|
+
if (Array.isArray(value)) {
|
|
34
|
+
return value.map((v) => walkAndRedact(v, totals));
|
|
35
|
+
}
|
|
36
|
+
if (value !== null && typeof value === 'object') {
|
|
37
|
+
const out = {};
|
|
38
|
+
for (const [k, v] of Object.entries(value)) {
|
|
39
|
+
out[k] = walkAndRedact(v, totals);
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=post-tool-use.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-tool-use.js","sourceRoot":"","sources":["../../../src/hooks/post-tool-use.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAwB,MAAM,eAAe,CAAC;AAG7D;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAiB;IACjD,MAAM,QAAQ,GAAG,GAAG,CAAC,aAAa,CAAC;IACnC,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAExC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,MAAM,QAAQ,GAAG,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACjD,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEnC,OAAO;QACL,kBAAkB,EAAE;YAClB,aAAa,EAAE,aAAa;YAC5B,mBAAmB,EAAE,QAA4C;SAClE;KACF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc,EAAE,MAA2B;IACvE,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,CAAC,GAAoB,MAAM,CAAC,KAAK,CAAC,CAAC;QACzC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU;YAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QAC1F,OAAO,CAAC,CAAC,IAAI,CAAC;IAChB,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,GAAG,GAA4B,EAAE,CAAC;QACxC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,EAAE,CAAC;YACtE,GAAG,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACpC,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type BlockerOpts } from './path-blocker.js';
|
|
2
|
+
import type { BlockDecision, HookEnvelope } from './protocol.js';
|
|
3
|
+
/**
|
|
4
|
+
* Decide whether a PreToolUse event should be blocked. Pure function; the
|
|
5
|
+
* bin entrypoint wraps this with stdin/stdout handling.
|
|
6
|
+
*/
|
|
7
|
+
export declare function decidePreToolUse(env: HookEnvelope, opts?: BlockerOpts): BlockDecision | null;
|
|
8
|
+
//# sourceMappingURL=pre-tool-use.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pre-tool-use.d.ts","sourceRoot":"","sources":["../../../src/hooks/pre-tool-use.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAEjE;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,GAAE,WAAgB,GAAG,aAAa,GAAG,IAAI,CA+BhG"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { scanBashCommand } from './command-scanner.js';
|
|
2
|
+
import { isBlockedPath } from './path-blocker.js';
|
|
3
|
+
/**
|
|
4
|
+
* Decide whether a PreToolUse event should be blocked. Pure function; the
|
|
5
|
+
* bin entrypoint wraps this with stdin/stdout handling.
|
|
6
|
+
*/
|
|
7
|
+
export function decidePreToolUse(env, opts = {}) {
|
|
8
|
+
const toolName = env.tool_name;
|
|
9
|
+
const input = env.tool_input ?? {};
|
|
10
|
+
if (toolName === 'Read') {
|
|
11
|
+
const path = typeof input['file_path'] === 'string' ? input['file_path'] : undefined;
|
|
12
|
+
if (path === undefined)
|
|
13
|
+
return null;
|
|
14
|
+
const d = isBlockedPath(path, opts);
|
|
15
|
+
if (d.blocked) {
|
|
16
|
+
return {
|
|
17
|
+
decision: 'block',
|
|
18
|
+
reason: `sigil ward: ${path} matches blocked pattern ${d.matchedPattern}. This path is on sigil's wardlist (~/.sigil/wards.toml + built-in defaults). If you need to read a file like this, edit the wardlist.`,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
if (toolName === 'Bash') {
|
|
24
|
+
const cmd = typeof input['command'] === 'string' ? input['command'] : undefined;
|
|
25
|
+
if (cmd === undefined)
|
|
26
|
+
return null;
|
|
27
|
+
const d = scanBashCommand(cmd, opts);
|
|
28
|
+
if (d.blocked) {
|
|
29
|
+
return {
|
|
30
|
+
decision: 'block',
|
|
31
|
+
reason: `sigil ward: bash command blocked — ${d.reason ?? 'matched blocked pattern'}.`,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return null; // not a tool we ward
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=pre-tool-use.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pre-tool-use.js","sourceRoot":"","sources":["../../../src/hooks/pre-tool-use.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAoB,MAAM,mBAAmB,CAAC;AAGpE;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAiB,EAAE,OAAoB,EAAE;IACxE,MAAM,QAAQ,GAAG,GAAG,CAAC,SAAS,CAAC;IAC/B,MAAM,KAAK,GAAG,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC;IAEnC,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,OAAO,KAAK,CAAC,WAAW,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACrF,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QACpC,MAAM,CAAC,GAAG,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACpC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;YACd,OAAO;gBACL,QAAQ,EAAE,OAAO;gBACjB,MAAM,EAAE,eAAe,IAAI,4BAA4B,CAAC,CAAC,cAAc,wIAAwI;aAChN,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;QACxB,MAAM,GAAG,GAAG,OAAO,KAAK,CAAC,SAAS,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAChF,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QACnC,MAAM,CAAC,GAAG,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;YACd,OAAO;gBACL,QAAQ,EAAE,OAAO;gBACjB,MAAM,EAAE,sCAAsC,CAAC,CAAC,MAAM,IAAI,yBAAyB,GAAG;aACvF,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,IAAI,CAAC,CAAC,qBAAqB;AACpC,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code hook I/O protocol. Hooks are spawned as subprocesses; they
|
|
3
|
+
* receive a JSON envelope on stdin and may write a JSON decision on stdout.
|
|
4
|
+
*
|
|
5
|
+
* Reference: https://docs.claude.com/en/docs/claude-code/hooks (subject to
|
|
6
|
+
* change; the fields below are what sigil's hooks read and write).
|
|
7
|
+
*/
|
|
8
|
+
export interface HookEnvelope {
|
|
9
|
+
session_id?: string;
|
|
10
|
+
transcript_path?: string;
|
|
11
|
+
hook_event_name?: string;
|
|
12
|
+
tool_name?: string;
|
|
13
|
+
tool_input?: Record<string, unknown>;
|
|
14
|
+
tool_response?: Record<string, unknown> | string;
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Decision emitted by a PreToolUse hook. Returning `{ decision: "block" }`
|
|
19
|
+
* stops the tool call and surfaces `reason` to the model.
|
|
20
|
+
*/
|
|
21
|
+
export interface BlockDecision {
|
|
22
|
+
decision: 'block';
|
|
23
|
+
reason: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Output from a PostToolUse hook that wants to modify the tool's response
|
|
27
|
+
* (e.g., redact secrets). The exact shape Claude Code accepts is in flux;
|
|
28
|
+
* we emit a structure that has been stable in recent versions.
|
|
29
|
+
*/
|
|
30
|
+
export interface PostToolModification {
|
|
31
|
+
hookSpecificOutput: {
|
|
32
|
+
hookEventName: 'PostToolUse';
|
|
33
|
+
updatedToolResponse: Record<string, unknown> | string;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Read the entire stdin stream and parse it as JSON. Returns an empty
|
|
38
|
+
* object if stdin closes without data.
|
|
39
|
+
*/
|
|
40
|
+
export declare function readHookEnvelope(stdin?: NodeJS.ReadableStream): Promise<HookEnvelope>;
|
|
41
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../src/hooks/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC;IAEjD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,kBAAkB,EAAE;QAClB,aAAa,EAAE,aAAa,CAAC;QAC7B,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC;KACvD,CAAC;CACH;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,GAAE,MAAM,CAAC,cAA8B,GAAG,OAAO,CAAC,YAAY,CAAC,CAY1G"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code hook I/O protocol. Hooks are spawned as subprocesses; they
|
|
3
|
+
* receive a JSON envelope on stdin and may write a JSON decision on stdout.
|
|
4
|
+
*
|
|
5
|
+
* Reference: https://docs.claude.com/en/docs/claude-code/hooks (subject to
|
|
6
|
+
* change; the fields below are what sigil's hooks read and write).
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Read the entire stdin stream and parse it as JSON. Returns an empty
|
|
10
|
+
* object if stdin closes without data.
|
|
11
|
+
*/
|
|
12
|
+
export async function readHookEnvelope(stdin = process.stdin) {
|
|
13
|
+
const chunks = [];
|
|
14
|
+
for await (const c of stdin) {
|
|
15
|
+
chunks.push(typeof c === 'string' ? Buffer.from(c, 'utf8') : c);
|
|
16
|
+
}
|
|
17
|
+
const text = Buffer.concat(chunks).toString('utf8').trim();
|
|
18
|
+
if (text.length === 0)
|
|
19
|
+
return {};
|
|
20
|
+
try {
|
|
21
|
+
return JSON.parse(text);
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
throw new Error(`hook stdin was not valid JSON: ${err.message}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../../src/hooks/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAkCH;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,QAA+B,OAAO,CAAC,KAAK;IACjF,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,KAAK,EAAE,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QAC5B,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAW,CAAC,CAAC;IAC5E,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3D,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAiB,CAAC;IAC1C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,kCAAmC,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;IAC9E,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output redactor — the second line of defence behind the path blocker.
|
|
3
|
+
* If a key somehow ends up in tool output (because the agent read a file
|
|
4
|
+
* the blocker missed, or coaxed a key out of an API), redact it before it
|
|
5
|
+
* reaches the model's context.
|
|
6
|
+
*
|
|
7
|
+
* The rules are deliberately strict — false positives are tolerable
|
|
8
|
+
* (redacted noise in tool output is annoying; an exfiltrated key is fatal).
|
|
9
|
+
*/
|
|
10
|
+
export interface RedactionStat {
|
|
11
|
+
reason: string;
|
|
12
|
+
count: number;
|
|
13
|
+
}
|
|
14
|
+
export interface RedactionResult {
|
|
15
|
+
text: string;
|
|
16
|
+
redactions: RedactionStat[];
|
|
17
|
+
}
|
|
18
|
+
export declare function redact(text: string): RedactionResult;
|
|
19
|
+
//# sourceMappingURL=redactor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redactor.d.ts","sourceRoot":"","sources":["../../../src/hooks/redactor.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,aAAa,EAAE,CAAC;CAC7B;AAuDD,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAYpD"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output redactor — the second line of defence behind the path blocker.
|
|
3
|
+
* If a key somehow ends up in tool output (because the agent read a file
|
|
4
|
+
* the blocker missed, or coaxed a key out of an API), redact it before it
|
|
5
|
+
* reaches the model's context.
|
|
6
|
+
*
|
|
7
|
+
* The rules are deliberately strict — false positives are tolerable
|
|
8
|
+
* (redacted noise in tool output is annoying; an exfiltrated key is fatal).
|
|
9
|
+
*/
|
|
10
|
+
const RULES = Object.freeze([
|
|
11
|
+
// PEM-encoded private key blocks. Matched first so the inner base64 isn't
|
|
12
|
+
// also caught by the hex/JWT rules.
|
|
13
|
+
{
|
|
14
|
+
reason: 'pem-private-key',
|
|
15
|
+
regex: /-----BEGIN [A-Z0-9 ]*PRIVATE KEY[A-Z0-9 ]*-----[\s\S]+?-----END [A-Z0-9 ]*PRIVATE KEY[A-Z0-9 ]*-----/g,
|
|
16
|
+
},
|
|
17
|
+
// Generic PEM block — second-line check for "BEGIN ... KEY ...END ... KEY"
|
|
18
|
+
// that didn't say PRIVATE explicitly (some tools emit just "KEY").
|
|
19
|
+
{
|
|
20
|
+
reason: 'pem-key-block',
|
|
21
|
+
regex: /-----BEGIN [A-Z0-9 ]*KEY[A-Z0-9 ]*-----[\s\S]+?-----END [A-Z0-9 ]*KEY[A-Z0-9 ]*-----/g,
|
|
22
|
+
},
|
|
23
|
+
// 32-byte raw private keys as 0x-prefixed hex (most common Ethereum form).
|
|
24
|
+
// Word boundary on both sides so we don't grab a 0x... that happens to be
|
|
25
|
+
// a longer hash and continue past it.
|
|
26
|
+
{
|
|
27
|
+
reason: 'hex-private-key',
|
|
28
|
+
regex: /\b0x[0-9a-fA-F]{64}(?![0-9a-fA-F])/g,
|
|
29
|
+
},
|
|
30
|
+
// JWT (header.payload.signature where header starts with eyJ — base64url
|
|
31
|
+
// of {"...). Three url-safe-base64 sections separated by dots.
|
|
32
|
+
{
|
|
33
|
+
reason: 'jwt',
|
|
34
|
+
regex: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g,
|
|
35
|
+
},
|
|
36
|
+
// npm tokens (npm_ + ~36 chars) — same pattern they're emitted in.
|
|
37
|
+
{
|
|
38
|
+
reason: 'npm-token',
|
|
39
|
+
regex: /\bnpm_[A-Za-z0-9]{36,}\b/g,
|
|
40
|
+
},
|
|
41
|
+
// Anthropic / Claude API keys (sk-ant-...).
|
|
42
|
+
{
|
|
43
|
+
reason: 'anthropic-api-key',
|
|
44
|
+
regex: /\bsk-ant-[A-Za-z0-9_-]{20,}\b/g,
|
|
45
|
+
},
|
|
46
|
+
// OpenAI keys.
|
|
47
|
+
{
|
|
48
|
+
reason: 'openai-api-key',
|
|
49
|
+
regex: /\bsk-[A-Za-z0-9]{32,}\b/g,
|
|
50
|
+
},
|
|
51
|
+
// AWS access key IDs (AKIA + 16 chars) and secret access keys (40 base64-ish chars).
|
|
52
|
+
{
|
|
53
|
+
reason: 'aws-access-key-id',
|
|
54
|
+
regex: /\bAKIA[0-9A-Z]{16}\b/g,
|
|
55
|
+
},
|
|
56
|
+
]);
|
|
57
|
+
export function redact(text) {
|
|
58
|
+
const counts = new Map();
|
|
59
|
+
let out = text;
|
|
60
|
+
for (const rule of RULES) {
|
|
61
|
+
out = out.replace(rule.regex, () => {
|
|
62
|
+
counts.set(rule.reason, (counts.get(rule.reason) ?? 0) + 1);
|
|
63
|
+
return `<REDACTED:${rule.reason}>`;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
const redactions = [];
|
|
67
|
+
for (const [reason, count] of counts)
|
|
68
|
+
redactions.push({ reason, count });
|
|
69
|
+
return { text: out, redactions };
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=redactor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redactor.js","sourceRoot":"","sources":["../../../src/hooks/redactor.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAiBH,MAAM,KAAK,GAAoB,MAAM,CAAC,MAAM,CAAC;IAC3C,0EAA0E;IAC1E,oCAAoC;IACpC;QACE,MAAM,EAAE,iBAAiB;QACzB,KAAK,EAAE,uGAAuG;KAC/G;IACD,2EAA2E;IAC3E,mEAAmE;IACnE;QACE,MAAM,EAAE,eAAe;QACvB,KAAK,EAAE,uFAAuF;KAC/F;IACD,2EAA2E;IAC3E,0EAA0E;IAC1E,sCAAsC;IACtC;QACE,MAAM,EAAE,iBAAiB;QACzB,KAAK,EAAE,qCAAqC;KAC7C;IACD,yEAAyE;IACzE,+DAA+D;IAC/D;QACE,MAAM,EAAE,KAAK;QACb,KAAK,EAAE,wDAAwD;KAChE;IACD,mEAAmE;IACnE;QACE,MAAM,EAAE,WAAW;QACnB,KAAK,EAAE,2BAA2B;KACnC;IACD,4CAA4C;IAC5C;QACE,MAAM,EAAE,mBAAmB;QAC3B,KAAK,EAAE,gCAAgC;KACxC;IACD,eAAe;IACf;QACE,MAAM,EAAE,gBAAgB;QACxB,KAAK,EAAE,0BAA0B;KAClC;IACD,qFAAqF;IACrF;QACE,MAAM,EAAE,mBAAmB;QAC3B,KAAK,EAAE,uBAAuB;KAC/B;CACF,CAAC,CAAC;AAEH,MAAM,UAAU,MAAM,CAAC,IAAY;IACjC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,IAAI,GAAG,GAAG,IAAI,CAAC;IACf,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE;YACjC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC5D,OAAO,aAAa,IAAI,CAAC,MAAM,GAAG,CAAC;QACrC,CAAC,CAAC,CAAC;IACL,CAAC;IACD,MAAM,UAAU,GAAoB,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,MAAM;QAAE,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IACzE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { type McpId, type McpRequest, type McpNotification, type McpResponse, type McpSuccess, type McpError, type McpErrorBody, type ParseResult, type ToolInputSchema, type ToolDefinition, type ToolContent, type ToolResult, PROTOCOL_VERSION, SERVER_INFO, MCP_PARSE_ERROR, MCP_INVALID_REQUEST, MCP_METHOD_NOT_FOUND, MCP_INVALID_PARAMS, MCP_INTERNAL_ERROR, parseMessage, encodeSuccess, encodeError, } from './protocol.js';
|
|
2
|
+
export { type Tool, type ToolHandler, type ToolHandlerCtx, ToolError, TOOLS, findTool, } from './tools.js';
|
|
3
|
+
export { type McpServerOpts, type McpStdioOpts, type McpLogEvent, handleLine, runMcpStdio, } from './server.js';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/mcp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,KAAK,EACV,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,gBAAgB,EAChB,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,WAAW,GACZ,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,KAAK,IAAI,EACT,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,SAAS,EACT,KAAK,EACL,QAAQ,GACT,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,UAAU,EACV,WAAW,GACZ,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { PROTOCOL_VERSION, SERVER_INFO, MCP_PARSE_ERROR, MCP_INVALID_REQUEST, MCP_METHOD_NOT_FOUND, MCP_INVALID_PARAMS, MCP_INTERNAL_ERROR, parseMessage, encodeSuccess, encodeError, } from './protocol.js';
|
|
2
|
+
export { ToolError, TOOLS, findTool, } from './tools.js';
|
|
3
|
+
export { handleLine, runMcpStdio, } from './server.js';
|
|
4
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/mcp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAaL,gBAAgB,EAChB,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,WAAW,GACZ,MAAM,eAAe,CAAC;AACvB,OAAO,EAIL,SAAS,EACT,KAAK,EACL,QAAQ,GACT,MAAM,YAAY,CAAC;AACpB,OAAO,EAIL,UAAU,EACV,WAAW,GACZ,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal subset of the Model Context Protocol (MCP) wire format that sigil
|
|
3
|
+
* needs to act as a stdio tool server for Claude Code (and any other MCP
|
|
4
|
+
* client). MCP rides on JSON-RPC 2.0; this module defines the request,
|
|
5
|
+
* response, and notification shapes plus helpers for encoding.
|
|
6
|
+
*
|
|
7
|
+
* Spec reference: https://modelcontextprotocol.io/specification
|
|
8
|
+
*
|
|
9
|
+
* We implement: initialize, notifications/initialized, tools/list, tools/call.
|
|
10
|
+
* Everything else (resources, prompts, sampling, completions, logging,
|
|
11
|
+
* pagination cursors) is intentionally omitted.
|
|
12
|
+
*/
|
|
13
|
+
export declare const PROTOCOL_VERSION = "2025-06-18";
|
|
14
|
+
export declare const SERVER_INFO: {
|
|
15
|
+
readonly name: "sigil-mcp";
|
|
16
|
+
readonly version: "0.0.1";
|
|
17
|
+
};
|
|
18
|
+
export type McpId = string | number | null;
|
|
19
|
+
export interface McpRequest {
|
|
20
|
+
jsonrpc: '2.0';
|
|
21
|
+
id: McpId;
|
|
22
|
+
method: string;
|
|
23
|
+
params?: unknown;
|
|
24
|
+
}
|
|
25
|
+
export interface McpNotification {
|
|
26
|
+
jsonrpc: '2.0';
|
|
27
|
+
method: string;
|
|
28
|
+
params?: unknown;
|
|
29
|
+
}
|
|
30
|
+
export interface McpSuccess {
|
|
31
|
+
jsonrpc: '2.0';
|
|
32
|
+
id: McpId;
|
|
33
|
+
result: unknown;
|
|
34
|
+
}
|
|
35
|
+
export interface McpErrorBody {
|
|
36
|
+
code: number;
|
|
37
|
+
message: string;
|
|
38
|
+
data?: unknown;
|
|
39
|
+
}
|
|
40
|
+
export interface McpError {
|
|
41
|
+
jsonrpc: '2.0';
|
|
42
|
+
id: McpId;
|
|
43
|
+
error: McpErrorBody;
|
|
44
|
+
}
|
|
45
|
+
export type McpResponse = McpSuccess | McpError;
|
|
46
|
+
export declare const MCP_PARSE_ERROR = -32700;
|
|
47
|
+
export declare const MCP_INVALID_REQUEST = -32600;
|
|
48
|
+
export declare const MCP_METHOD_NOT_FOUND = -32601;
|
|
49
|
+
export declare const MCP_INVALID_PARAMS = -32602;
|
|
50
|
+
export declare const MCP_INTERNAL_ERROR = -32603;
|
|
51
|
+
export interface ToolInputSchema {
|
|
52
|
+
type: 'object';
|
|
53
|
+
properties: Record<string, unknown>;
|
|
54
|
+
required?: string[];
|
|
55
|
+
additionalProperties?: boolean;
|
|
56
|
+
}
|
|
57
|
+
export interface ToolDefinition {
|
|
58
|
+
name: string;
|
|
59
|
+
description: string;
|
|
60
|
+
inputSchema: ToolInputSchema;
|
|
61
|
+
}
|
|
62
|
+
export type ToolContent = {
|
|
63
|
+
type: 'text';
|
|
64
|
+
text: string;
|
|
65
|
+
};
|
|
66
|
+
export interface ToolResult {
|
|
67
|
+
content: ToolContent[];
|
|
68
|
+
/** When omitted, treated as false. */
|
|
69
|
+
isError?: boolean;
|
|
70
|
+
/** Structured payload for clients that want typed access; supplemental to content. */
|
|
71
|
+
structuredContent?: unknown;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Parse a single NDJSON line into an MCP envelope.
|
|
75
|
+
*
|
|
76
|
+
* Returns:
|
|
77
|
+
* - `{ kind: 'request', request }` for an incoming request (has id + method)
|
|
78
|
+
* - `{ kind: 'notification', notification }` for an incoming notification (method, no id)
|
|
79
|
+
* - `{ kind: 'parse_error' }` for non-JSON
|
|
80
|
+
* - `{ kind: 'invalid', id, reason }` for a JSON object that isn't a valid envelope
|
|
81
|
+
*/
|
|
82
|
+
export type ParseResult = {
|
|
83
|
+
kind: 'request';
|
|
84
|
+
request: McpRequest;
|
|
85
|
+
} | {
|
|
86
|
+
kind: 'notification';
|
|
87
|
+
notification: McpNotification;
|
|
88
|
+
} | {
|
|
89
|
+
kind: 'parse_error';
|
|
90
|
+
} | {
|
|
91
|
+
kind: 'invalid';
|
|
92
|
+
id: McpId;
|
|
93
|
+
reason: string;
|
|
94
|
+
};
|
|
95
|
+
export declare function parseMessage(line: string): ParseResult;
|
|
96
|
+
export declare function encodeSuccess(id: McpId, result: unknown): string;
|
|
97
|
+
export declare function encodeError(id: McpId, code: number, message: string, data?: unknown): string;
|
|
98
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../../src/mcp/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,gBAAgB,eAAe,CAAC;AAE7C,eAAO,MAAM,WAAW;;;CAGd,CAAC;AAEX,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;AAE3C,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,KAAK,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,YAAY,CAAC;CACrB;AAED,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,QAAQ,CAAC;AAGhD,eAAO,MAAM,eAAe,SAAS,CAAC;AACtC,eAAO,MAAM,mBAAmB,SAAS,CAAC;AAC1C,eAAO,MAAM,oBAAoB,SAAS,CAAC;AAC3C,eAAO,MAAM,kBAAkB,SAAS,CAAC;AACzC,eAAO,MAAM,kBAAkB,SAAS,CAAC;AAEzC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,QAAQ,CAAC;IACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,eAAe,CAAC;CAC9B;AAED,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnC,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,sCAAsC;IACtC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sFAAsF;IACtF,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,UAAU,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,YAAY,EAAE,eAAe,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAyCtD;AAMD,wBAAgB,aAAa,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAGhE;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAI5F"}
|