@intutic/sync-daemon 1.6.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/LICENSE +21 -0
- package/README.md +73 -0
- package/dist/brainIndexer.d.ts +51 -0
- package/dist/brainIndexer.d.ts.map +1 -0
- package/dist/brainIndexer.js +148 -0
- package/dist/brainIndexer.js.map +1 -0
- package/dist/configReader.d.ts +46 -0
- package/dist/configReader.d.ts.map +1 -0
- package/dist/configReader.js +133 -0
- package/dist/configReader.js.map +1 -0
- package/dist/configWriter.d.ts +53 -0
- package/dist/configWriter.d.ts.map +1 -0
- package/dist/configWriter.js +503 -0
- package/dist/configWriter.js.map +1 -0
- package/dist/harness/aiderConfigMerger.d.ts +27 -0
- package/dist/harness/aiderConfigMerger.d.ts.map +1 -0
- package/dist/harness/aiderConfigMerger.js +139 -0
- package/dist/harness/aiderConfigMerger.js.map +1 -0
- package/dist/harness/antigravityHooks.d.ts +43 -0
- package/dist/harness/antigravityHooks.d.ts.map +1 -0
- package/dist/harness/antigravityHooks.js +193 -0
- package/dist/harness/antigravityHooks.js.map +1 -0
- package/dist/harness/claudeCodeHooks.d.ts +59 -0
- package/dist/harness/claudeCodeHooks.d.ts.map +1 -0
- package/dist/harness/claudeCodeHooks.js +414 -0
- package/dist/harness/claudeCodeHooks.js.map +1 -0
- package/dist/harness/claudeDesktopHooks.d.ts +30 -0
- package/dist/harness/claudeDesktopHooks.d.ts.map +1 -0
- package/dist/harness/claudeDesktopHooks.js +267 -0
- package/dist/harness/claudeDesktopHooks.js.map +1 -0
- package/dist/harness/clineHooks.d.ts +23 -0
- package/dist/harness/clineHooks.d.ts.map +1 -0
- package/dist/harness/clineHooks.js +169 -0
- package/dist/harness/clineHooks.js.map +1 -0
- package/dist/harness/continueHooks.d.ts +33 -0
- package/dist/harness/continueHooks.d.ts.map +1 -0
- package/dist/harness/continueHooks.js +124 -0
- package/dist/harness/continueHooks.js.map +1 -0
- package/dist/harness/cursorHooks.d.ts +27 -0
- package/dist/harness/cursorHooks.d.ts.map +1 -0
- package/dist/harness/cursorHooks.js +221 -0
- package/dist/harness/cursorHooks.js.map +1 -0
- package/dist/harness/cursorHooksJson.d.ts +29 -0
- package/dist/harness/cursorHooksJson.d.ts.map +1 -0
- package/dist/harness/cursorHooksJson.js +130 -0
- package/dist/harness/cursorHooksJson.js.map +1 -0
- package/dist/harness/gooseHardener.d.ts +39 -0
- package/dist/harness/gooseHardener.d.ts.map +1 -0
- package/dist/harness/gooseHardener.js +152 -0
- package/dist/harness/gooseHardener.js.map +1 -0
- package/dist/harness/gooseHooks.d.ts +24 -0
- package/dist/harness/gooseHooks.d.ts.map +1 -0
- package/dist/harness/gooseHooks.js +175 -0
- package/dist/harness/gooseHooks.js.map +1 -0
- package/dist/harness/gooseHooksWriter.d.ts +27 -0
- package/dist/harness/gooseHooksWriter.d.ts.map +1 -0
- package/dist/harness/gooseHooksWriter.js +147 -0
- package/dist/harness/gooseHooksWriter.js.map +1 -0
- package/dist/harness/hermesHooks.d.ts +31 -0
- package/dist/harness/hermesHooks.d.ts.map +1 -0
- package/dist/harness/hermesHooks.js +245 -0
- package/dist/harness/hermesHooks.js.map +1 -0
- package/dist/harness/mcpAutoWrite.d.ts +35 -0
- package/dist/harness/mcpAutoWrite.d.ts.map +1 -0
- package/dist/harness/mcpAutoWrite.js +334 -0
- package/dist/harness/mcpAutoWrite.js.map +1 -0
- package/dist/harness/n8nHooks.d.ts +28 -0
- package/dist/harness/n8nHooks.d.ts.map +1 -0
- package/dist/harness/n8nHooks.js +205 -0
- package/dist/harness/n8nHooks.js.map +1 -0
- package/dist/harness/openWebuiHooks.d.ts +31 -0
- package/dist/harness/openWebuiHooks.d.ts.map +1 -0
- package/dist/harness/openWebuiHooks.js +166 -0
- package/dist/harness/openWebuiHooks.js.map +1 -0
- package/dist/harness/openclawHooks.d.ts +34 -0
- package/dist/harness/openclawHooks.d.ts.map +1 -0
- package/dist/harness/openclawHooks.js +296 -0
- package/dist/harness/openclawHooks.js.map +1 -0
- package/dist/harness/openhandsHooks.d.ts +24 -0
- package/dist/harness/openhandsHooks.d.ts.map +1 -0
- package/dist/harness/openhandsHooks.js +178 -0
- package/dist/harness/openhandsHooks.js.map +1 -0
- package/dist/harness/openhandsHooksWriter.d.ts +23 -0
- package/dist/harness/openhandsHooksWriter.d.ts.map +1 -0
- package/dist/harness/openhandsHooksWriter.js +146 -0
- package/dist/harness/openhandsHooksWriter.js.map +1 -0
- package/dist/harness/piHooks.d.ts +32 -0
- package/dist/harness/piHooks.d.ts.map +1 -0
- package/dist/harness/piHooks.js +257 -0
- package/dist/harness/piHooks.js.map +1 -0
- package/dist/harness/rooCodeHooks.d.ts +31 -0
- package/dist/harness/rooCodeHooks.d.ts.map +1 -0
- package/dist/harness/rooCodeHooks.js +300 -0
- package/dist/harness/rooCodeHooks.js.map +1 -0
- package/dist/harness/windsurfHooks.d.ts +27 -0
- package/dist/harness/windsurfHooks.d.ts.map +1 -0
- package/dist/harness/windsurfHooks.js +202 -0
- package/dist/harness/windsurfHooks.js.map +1 -0
- package/dist/hashReporter.d.ts +33 -0
- package/dist/hashReporter.d.ts.map +1 -0
- package/dist/hashReporter.js +69 -0
- package/dist/hashReporter.js.map +1 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/integrityStore.d.ts +43 -0
- package/dist/integrityStore.d.ts.map +1 -0
- package/dist/integrityStore.js +94 -0
- package/dist/integrityStore.js.map +1 -0
- package/dist/lib/complianceProbes.d.ts +39 -0
- package/dist/lib/complianceProbes.d.ts.map +1 -0
- package/dist/lib/complianceProbes.js +115 -0
- package/dist/lib/complianceProbes.js.map +1 -0
- package/dist/lib/processPoller.d.ts +2 -0
- package/dist/lib/processPoller.d.ts.map +1 -0
- package/dist/lib/processPoller.js +87 -0
- package/dist/lib/processPoller.js.map +1 -0
- package/dist/lib/runtimeEnv.d.ts +49 -0
- package/dist/lib/runtimeEnv.d.ts.map +1 -0
- package/dist/lib/runtimeEnv.js +70 -0
- package/dist/lib/runtimeEnv.js.map +1 -0
- package/dist/skillWriter.d.ts +24 -0
- package/dist/skillWriter.d.ts.map +1 -0
- package/dist/skillWriter.js +112 -0
- package/dist/skillWriter.js.map +1 -0
- package/dist/syncLoop.d.ts +70 -0
- package/dist/syncLoop.d.ts.map +1 -0
- package/dist/syncLoop.js +546 -0
- package/dist/syncLoop.js.map +1 -0
- package/dist/trajectoryMonitor.d.ts +126 -0
- package/dist/trajectoryMonitor.d.ts.map +1 -0
- package/dist/trajectoryMonitor.js +291 -0
- package/dist/trajectoryMonitor.js.map +1 -0
- package/dist/watcher/driftWatcher.d.ts +25 -0
- package/dist/watcher/driftWatcher.d.ts.map +1 -0
- package/dist/watcher/driftWatcher.js +72 -0
- package/dist/watcher/driftWatcher.js.map +1 -0
- package/dist/watcher/settingsGuard.d.ts +32 -0
- package/dist/watcher/settingsGuard.d.ts.map +1 -0
- package/dist/watcher/settingsGuard.js +250 -0
- package/dist/watcher/settingsGuard.js.map +1 -0
- package/dist/wsClient.d.ts +49 -0
- package/dist/wsClient.d.ts.map +1 -0
- package/dist/wsClient.js +149 -0
- package/dist/wsClient.js.map +1 -0
- package/package.json +96 -0
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* claudeCodeHooks.ts — Claude Code PreToolUse hook generator.
|
|
3
|
+
*
|
|
4
|
+
* Compiles SOP evaluation criteria and high risk tools into native Claude Code
|
|
5
|
+
* settings.json configurations (both permissions.deny rules and PreToolUse hooks).
|
|
6
|
+
* Writes configurations locally to the workspace and globally to ~/.claude/.
|
|
7
|
+
*
|
|
8
|
+
* LLD #14 — claudeCodeHooks.ts
|
|
9
|
+
* HLD §3.14 — Three-Tier Defense Cascade (Tier 1 Native Gating)
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import * as node_fs from 'node:fs/promises';
|
|
14
|
+
import * as node_path from 'node:path';
|
|
15
|
+
import * as node_os from 'node:os';
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
import { createLogger } from '@intutic/logger';
|
|
18
|
+
const log = createLogger('sync-claude-hooks');
|
|
19
|
+
/** Path to the local hook event log file appended by generated hook scripts. */
|
|
20
|
+
export const HOOK_EVENTS_LOG = '.intutic/events/hook-events.jsonl';
|
|
21
|
+
// Zod schema to parse SOP evaluation criteria and tool restrictions
|
|
22
|
+
export const SopHookConstraintsSchema = z.object({
|
|
23
|
+
highRiskTools: z.array(z.string()).default([]),
|
|
24
|
+
patterns: z.array(z.string()).default([]),
|
|
25
|
+
});
|
|
26
|
+
/**
|
|
27
|
+
* Extracts constraints from SOP registry entries.
|
|
28
|
+
* Looks inside SOP markdown content for structured JSON/YAML blocks or parses settings.
|
|
29
|
+
*/
|
|
30
|
+
export function parseSopConstraints(sops, settings) {
|
|
31
|
+
const highRiskToolsSet = new Set();
|
|
32
|
+
const patternsSet = new Set();
|
|
33
|
+
// 1. Parse from workspace settings if present
|
|
34
|
+
// Treat settings as Record<string, unknown> for legacy field access (highRiskTools, patterns)
|
|
35
|
+
if (settings) {
|
|
36
|
+
const s = settings;
|
|
37
|
+
if (Array.isArray(s['highRiskTools'])) {
|
|
38
|
+
s['highRiskTools'].forEach((t) => typeof t === 'string' && highRiskToolsSet.add(t));
|
|
39
|
+
}
|
|
40
|
+
if (Array.isArray(s['patterns'])) {
|
|
41
|
+
s['patterns'].forEach((p) => typeof p === 'string' && patternsSet.add(p));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// 2. Parse from SOP content
|
|
45
|
+
for (const sop of sops) {
|
|
46
|
+
const content = sop.content;
|
|
47
|
+
const startIdx = content.indexOf('```json');
|
|
48
|
+
if (startIdx !== -1) {
|
|
49
|
+
const jsonStart = content.indexOf('\n', startIdx);
|
|
50
|
+
const endIdx = content.indexOf('```', jsonStart !== -1 ? jsonStart : startIdx + 7);
|
|
51
|
+
if (jsonStart !== -1 && endIdx !== -1 && endIdx > jsonStart) {
|
|
52
|
+
const jsonText = content.slice(jsonStart + 1, endIdx).trim();
|
|
53
|
+
try {
|
|
54
|
+
const parsed = JSON.parse(jsonText);
|
|
55
|
+
const validated = SopHookConstraintsSchema.safeParse(parsed);
|
|
56
|
+
if (validated.success) {
|
|
57
|
+
validated.data.highRiskTools.forEach((t) => highRiskToolsSet.add(t));
|
|
58
|
+
validated.data.patterns.forEach((p) => patternsSet.add(p));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Ignore parsing failures
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Also look for inline patterns
|
|
67
|
+
// e.g., "Blacklist Pattern: rm -rf"
|
|
68
|
+
const patternRegexes = [/Blacklist Pattern:\s*`?([^`\n]+)`?/gi, /Deny Pattern:\s*`?([^`\n]+)`?/gi];
|
|
69
|
+
for (const regex of patternRegexes) {
|
|
70
|
+
let match;
|
|
71
|
+
while ((match = regex.exec(content)) !== null) {
|
|
72
|
+
if (match[1]) {
|
|
73
|
+
patternsSet.add(match[1].trim());
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// e.g., "High Risk Tool: Bash"
|
|
78
|
+
const toolRegex = /High Risk Tool:\s*`?([^`\n]+)`?/gi;
|
|
79
|
+
let toolMatch;
|
|
80
|
+
while ((toolMatch = toolRegex.exec(content)) !== null) {
|
|
81
|
+
if (toolMatch[1]) {
|
|
82
|
+
highRiskToolsSet.add(toolMatch[1].trim());
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
highRiskTools: Array.from(highRiskToolsSet),
|
|
88
|
+
patterns: Array.from(patternsSet),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Compiles constraints into Claude Code settings.json format and writes it.
|
|
93
|
+
*
|
|
94
|
+
* @param workspaceRoot - Workspace root path.
|
|
95
|
+
* @param sops - The sync SOP list.
|
|
96
|
+
* @param settings - Passthrough settings.
|
|
97
|
+
* @param controlPlaneUrl - Control plane base URL embedded into the hook events log path comment.
|
|
98
|
+
* @param workspaceId - Workspace ID embedded into hook event payloads.
|
|
99
|
+
* @param harnessType - Harness type tag written into each hook event.
|
|
100
|
+
*/
|
|
101
|
+
export async function updatePreToolUseHooks(workspaceRoot, sops, settings, controlPlaneUrl, workspaceId, harnessType = 'claude-code') {
|
|
102
|
+
const constraints = parseSopConstraints(sops, settings);
|
|
103
|
+
log.info({ action: 'update_hooks', toolCount: constraints.highRiskTools.length, patternCount: constraints.patterns.length }, 'Compiling PreToolUse hook configurations for Claude Code');
|
|
104
|
+
// Build the permissions.deny rules and hooks structure
|
|
105
|
+
const denyRules = [];
|
|
106
|
+
// High risk tools deny rules
|
|
107
|
+
for (const tool of constraints.highRiskTools) {
|
|
108
|
+
denyRules.push(tool);
|
|
109
|
+
}
|
|
110
|
+
// Pattern-based deny rules (e.g. Bash(rm -rf *))
|
|
111
|
+
for (const pattern of constraints.patterns) {
|
|
112
|
+
denyRules.push(`Bash(*${pattern}*)`);
|
|
113
|
+
}
|
|
114
|
+
// Build settings object matching Claude Code schema.
|
|
115
|
+
// We hook Bash, Edit, Write, and MultiEdit so that the policy gate
|
|
116
|
+
// covers file-modification tools in addition to shell execution.
|
|
117
|
+
const hookEntry = (matcher) => ({
|
|
118
|
+
matcher,
|
|
119
|
+
hooks: [
|
|
120
|
+
{
|
|
121
|
+
type: 'command',
|
|
122
|
+
command: `node ${node_path.join(workspaceRoot, '.intutic', 'hooks', 'pre-tool-check.js')}`,
|
|
123
|
+
timeout: 10,
|
|
124
|
+
statusMessage: 'Verifying tool execution against Intutic SOP policy...',
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
});
|
|
128
|
+
const newSettings = {
|
|
129
|
+
permissions: {
|
|
130
|
+
deny: denyRules,
|
|
131
|
+
},
|
|
132
|
+
hooks: {
|
|
133
|
+
PreToolUse: [
|
|
134
|
+
hookEntry('Bash'),
|
|
135
|
+
hookEntry('Edit'),
|
|
136
|
+
hookEntry('Write'),
|
|
137
|
+
hookEntry('MultiEdit'),
|
|
138
|
+
],
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
// Create hook script locally in workspace root (.intutic/hooks/pre-tool-check.js)
|
|
142
|
+
const hookScriptDir = node_path.join(workspaceRoot, '.intutic', 'hooks');
|
|
143
|
+
await node_fs.mkdir(hookScriptDir, { recursive: true });
|
|
144
|
+
// Ensure hook events log directory exists
|
|
145
|
+
const hookEventsDir = node_path.join(workspaceRoot, '.intutic', 'events');
|
|
146
|
+
await node_fs.mkdir(hookEventsDir, { recursive: true });
|
|
147
|
+
const hookEventsLog = node_path.join(hookEventsDir, 'hook-events.jsonl');
|
|
148
|
+
const hookScriptContent = `
|
|
149
|
+
/**
|
|
150
|
+
* Intutic PreToolUse execution gate.
|
|
151
|
+
* Auto-generated by intutic sync-daemon. DO NOT EDIT.
|
|
152
|
+
*
|
|
153
|
+
* Governance events are appended to .intutic/events/hook-events.jsonl
|
|
154
|
+
* and drained to the control plane by the sync-daemon on each cycle.
|
|
155
|
+
* Control plane: ${controlPlaneUrl ?? 'https://api.intutic.ai'}
|
|
156
|
+
*/
|
|
157
|
+
const path = require('path');
|
|
158
|
+
const os = require('os');
|
|
159
|
+
const fs = require('fs');
|
|
160
|
+
const crypto = require('crypto');
|
|
161
|
+
const https = require('https');
|
|
162
|
+
|
|
163
|
+
// ── Runtime credentials (sourced from ~/.intutic/env/runtime.env at invocation time) ──
|
|
164
|
+
const _runtimeEnvPath = path.join(os.homedir(), '.intutic', 'env', 'runtime.env');
|
|
165
|
+
let _intuticHost = 'https://api.intutic.ai', _intuticKey = '', _intuticWsId = ${JSON.stringify(workspaceId ?? '')};
|
|
166
|
+
try {
|
|
167
|
+
fs.readFileSync(_runtimeEnvPath, 'utf-8').split('\\n').forEach(line => {
|
|
168
|
+
const eq = line.indexOf('='); if (eq < 0) return;
|
|
169
|
+
const k = line.slice(0, eq).trim(), v = line.slice(eq + 1).trim();
|
|
170
|
+
if (k === 'INTUTIC_HOST' && v) _intuticHost = v;
|
|
171
|
+
if (k === 'INTUTIC_API_KEY' && v) _intuticKey = v;
|
|
172
|
+
if (k === 'INTUTIC_WORKSPACE_ID' && v) _intuticWsId = v;
|
|
173
|
+
});
|
|
174
|
+
} catch { /* runtime.env not yet written — use defaults */ }
|
|
175
|
+
|
|
176
|
+
// Governance-sensitive paths that no agent may modify.
|
|
177
|
+
// Editing these files is how privilege escalation occurs.
|
|
178
|
+
const PROTECTED_PATHS = [
|
|
179
|
+
path.join(os.homedir(), '.claude', 'settings.json'),
|
|
180
|
+
path.join(os.homedir(), '.claude', 'settings.local.json'),
|
|
181
|
+
'.claude/settings.json',
|
|
182
|
+
'.claude/settings.local.json',
|
|
183
|
+
'.intutic/hooks',
|
|
184
|
+
'.intutic/integrity.json',
|
|
185
|
+
'.intutic/context_integrity.json',
|
|
186
|
+
];
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Appends a governance event to the local hook-events log.
|
|
190
|
+
* Uses synchronous append (O_APPEND is atomic for single-write < PIPE_BUF).
|
|
191
|
+
* The sync-daemon drains this file on each cycle and POSTs to the control plane.
|
|
192
|
+
*/
|
|
193
|
+
function logEvent(verdict, toolName, reason) {
|
|
194
|
+
try {
|
|
195
|
+
const ts = new Date().toISOString();
|
|
196
|
+
const incidentId = crypto.createHash('sha1').update(ts + toolName + _intuticWsId).digest('hex').slice(0, 16);
|
|
197
|
+
const entry = JSON.stringify({
|
|
198
|
+
event: verdict === 'blocked' ? 'tool_blocked' : 'tool_allowed',
|
|
199
|
+
toolName,
|
|
200
|
+
reason: reason || '',
|
|
201
|
+
workspaceId: _intuticWsId,
|
|
202
|
+
harnessType: ${JSON.stringify(harnessType)},
|
|
203
|
+
timestamp: ts,
|
|
204
|
+
incidentId,
|
|
205
|
+
}) + '\\n';
|
|
206
|
+
// Path B: reliable file append (sync-daemon drains on FSEvents change)
|
|
207
|
+
fs.appendFileSync(${JSON.stringify(hookEventsLog)}, entry, { flag: 'a' });
|
|
208
|
+
// Path A: fire-and-forget HTTP POST (near-real-time dashboard, non-blocking)
|
|
209
|
+
if (_intuticKey) {
|
|
210
|
+
try {
|
|
211
|
+
const body = JSON.stringify({ events: [JSON.parse(entry)] });
|
|
212
|
+
const urlObj = new URL('/api/v1/hook-events', _intuticHost);
|
|
213
|
+
const isHttps = urlObj.protocol === 'https:';
|
|
214
|
+
const mod = isHttps ? https : require('http');
|
|
215
|
+
const req = mod.request({
|
|
216
|
+
hostname: urlObj.hostname,
|
|
217
|
+
port: urlObj.port || (isHttps ? 443 : 80),
|
|
218
|
+
path: urlObj.pathname,
|
|
219
|
+
method: 'POST',
|
|
220
|
+
headers: {
|
|
221
|
+
'Content-Type': 'application/json',
|
|
222
|
+
'Content-Length': Buffer.byteLength(body),
|
|
223
|
+
'Authorization': 'Bearer ' + _intuticKey,
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
req.on('error', () => { /* fire-and-forget — ignore errors */ });
|
|
227
|
+
req.write(body);
|
|
228
|
+
req.end();
|
|
229
|
+
} catch { /* never crash the hook */ }
|
|
230
|
+
}
|
|
231
|
+
} catch { /* never crash the hook */ }
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Read stdin containing Claude's tool context
|
|
235
|
+
let inputData = '';
|
|
236
|
+
process.stdin.on('data', (chunk) => { inputData += chunk; });
|
|
237
|
+
process.stdin.on('end', () => {
|
|
238
|
+
try {
|
|
239
|
+
const ctx = JSON.parse(inputData);
|
|
240
|
+
const toolName = (ctx.tool_name || ctx.toolName || '').toLowerCase();
|
|
241
|
+
const toolInput = ctx.tool_input || ctx.toolInput || {};
|
|
242
|
+
const toolInputStr = JSON.stringify(toolInput);
|
|
243
|
+
|
|
244
|
+
// 1. Protected-path guard — block any file-modification tool targeting
|
|
245
|
+
// governance-sensitive paths regardless of SOP pattern list.
|
|
246
|
+
if (['edit', 'write', 'multiedit'].includes(toolName)) {
|
|
247
|
+
const targetPath = toolInput.path || toolInput.file_path ||
|
|
248
|
+
toolInput.new_path || toolInput.target || '';
|
|
249
|
+
for (const p of PROTECTED_PATHS) {
|
|
250
|
+
if (targetPath.includes(p) || String(targetPath).startsWith(p)) {
|
|
251
|
+
const reason = \`Attempt to modify governance-protected path: "\${targetPath}". \` +
|
|
252
|
+
'To change governance policy, update your SOP via the Intutic control plane.';
|
|
253
|
+
console.error(\`[Intutic Guardrail] BLOCKED: \${reason}\`);
|
|
254
|
+
logEvent('blocked', toolName, reason);
|
|
255
|
+
process.exit(2);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// 2. SOP-compiled pattern blacklist
|
|
261
|
+
const patterns = ${JSON.stringify(constraints.patterns)};
|
|
262
|
+
for (const pattern of patterns) {
|
|
263
|
+
if (toolInputStr.includes(pattern)) {
|
|
264
|
+
const reason = \`Policy violation matching pattern: "\${pattern}"\`;
|
|
265
|
+
console.error(\`[Intutic Guardrail] Blocked execution of tool "\${toolName}" due to \${reason}\`);
|
|
266
|
+
logEvent('blocked', toolName, reason);
|
|
267
|
+
process.exit(2);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
logEvent('allowed', toolName, '');
|
|
272
|
+
process.exit(0);
|
|
273
|
+
} catch (err) {
|
|
274
|
+
// Fail CLOSED — any hook execution error blocks the tool call.
|
|
275
|
+
// The sync daemon will rewrite this hook on the next sync cycle.
|
|
276
|
+
console.error('[Intutic Guardrail] Hook error (blocking for safety):', err);
|
|
277
|
+
logEvent('blocked', 'unknown', String(err));
|
|
278
|
+
process.exit(2);
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
`.trim();
|
|
282
|
+
await node_fs.writeFile(node_path.join(hookScriptDir, 'pre-tool-check.js'), hookScriptContent, 'utf-8');
|
|
283
|
+
// Write local settings config
|
|
284
|
+
const localClaudeDir = node_path.join(workspaceRoot, '.claude');
|
|
285
|
+
await node_fs.mkdir(localClaudeDir, { recursive: true });
|
|
286
|
+
const localSettingsPath = node_path.join(localClaudeDir, 'settings.json');
|
|
287
|
+
let existingLocal = {};
|
|
288
|
+
try {
|
|
289
|
+
const raw = await node_fs.readFile(localSettingsPath, 'utf-8');
|
|
290
|
+
existingLocal = JSON.parse(raw);
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
// Ignore
|
|
294
|
+
}
|
|
295
|
+
const mergedLocal = {
|
|
296
|
+
...existingLocal,
|
|
297
|
+
permissions: {
|
|
298
|
+
...existingLocal.permissions,
|
|
299
|
+
deny: denyRules,
|
|
300
|
+
},
|
|
301
|
+
hooks: {
|
|
302
|
+
...existingLocal.hooks,
|
|
303
|
+
PreToolUse: newSettings.hooks.PreToolUse,
|
|
304
|
+
},
|
|
305
|
+
};
|
|
306
|
+
await node_fs.writeFile(localSettingsPath, JSON.stringify(mergedLocal, null, 2) + '\n', 'utf-8');
|
|
307
|
+
// Write global settings config ~/.claude/settings.json
|
|
308
|
+
//
|
|
309
|
+
// GUARD (bugfix): never touch the user's real global settings from a test
|
|
310
|
+
// run. Vitest sets VITEST=1; without this guard, every run of
|
|
311
|
+
// __tests__/harness/claudeCodeHooks.test.ts merged the MOCK SOP denies
|
|
312
|
+
// (Bash/Write/rm -rf/…) into the developer's real ~/.claude/settings.json
|
|
313
|
+
// and pointed global hooks at a mkdtemp dir the test then deleted —
|
|
314
|
+
// leaving dead fail-closed hooks and a denied Bash/Write toolset.
|
|
315
|
+
// INTUTIC_SKIP_GLOBAL_HOOKS=1 is an explicit opt-out for dev machines.
|
|
316
|
+
if (process.env['VITEST'] || process.env['INTUTIC_SKIP_GLOBAL_HOOKS']) {
|
|
317
|
+
log.info({ action: 'global_hooks_skipped' }, 'Skipping global ~/.claude settings write (test or opt-out environment)');
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const globalClaudeDir = node_path.join(node_os.homedir(), '.claude');
|
|
321
|
+
await node_fs.mkdir(globalClaudeDir, { recursive: true });
|
|
322
|
+
const globalSettingsPath = node_path.join(globalClaudeDir, 'settings.json');
|
|
323
|
+
let existingGlobal = {};
|
|
324
|
+
try {
|
|
325
|
+
const raw = await node_fs.readFile(globalSettingsPath, 'utf-8');
|
|
326
|
+
existingGlobal = JSON.parse(raw);
|
|
327
|
+
}
|
|
328
|
+
catch {
|
|
329
|
+
// Ignore
|
|
330
|
+
}
|
|
331
|
+
// Merge, prioritizing global user settings but updating permissions/hooks
|
|
332
|
+
const mergedGlobal = {
|
|
333
|
+
...existingGlobal,
|
|
334
|
+
permissions: {
|
|
335
|
+
...existingGlobal.permissions,
|
|
336
|
+
deny: Array.from(new Set([...(existingGlobal.permissions?.deny || []), ...denyRules])),
|
|
337
|
+
},
|
|
338
|
+
hooks: {
|
|
339
|
+
...existingGlobal.hooks,
|
|
340
|
+
PreToolUse: newSettings.hooks.PreToolUse,
|
|
341
|
+
},
|
|
342
|
+
};
|
|
343
|
+
await node_fs.writeFile(globalSettingsPath, JSON.stringify(mergedGlobal, null, 2) + '\n', 'utf-8');
|
|
344
|
+
log.info({ action: 'hooks_written' }, 'Successfully updated settings.json hooks globally and locally');
|
|
345
|
+
}
|
|
346
|
+
// ─── Event Drain Helper ──────────────────────────────────────────────────────
|
|
347
|
+
/**
|
|
348
|
+
* Drains the local hook-events log file and POSTs all accumulated governance
|
|
349
|
+
* events to the control plane in a single batch request.
|
|
350
|
+
*
|
|
351
|
+
* Called by the sync-daemon's main loop (syncLoop.ts) on every cycle, after
|
|
352
|
+
* the main sync operations. On success, the log file is truncated to prevent
|
|
353
|
+
* unbounded growth. On network failure, events remain in the log and will be
|
|
354
|
+
* retried on the next cycle.
|
|
355
|
+
*
|
|
356
|
+
* @param workspaceRoot - Workspace root (log file is at workspaceRoot/.intutic/events/hook-events.jsonl)
|
|
357
|
+
* @param controlPlaneUrl - Control plane base URL
|
|
358
|
+
* @param apiKey - Workspace API key (Bearer token)
|
|
359
|
+
* @returns number of events drained (0 if log was empty or network failed)
|
|
360
|
+
*/
|
|
361
|
+
export async function drainHookEvents(workspaceRoot, controlPlaneUrl, apiKey) {
|
|
362
|
+
const logPath = node_path.join(workspaceRoot, '.intutic', 'events', 'hook-events.jsonl');
|
|
363
|
+
let raw;
|
|
364
|
+
try {
|
|
365
|
+
raw = await node_fs.readFile(logPath, 'utf-8');
|
|
366
|
+
}
|
|
367
|
+
catch {
|
|
368
|
+
return 0; // File doesn't exist yet — nothing to drain
|
|
369
|
+
}
|
|
370
|
+
const lines = raw.trim().split('\n').filter(Boolean);
|
|
371
|
+
if (lines.length === 0)
|
|
372
|
+
return 0;
|
|
373
|
+
const events = [];
|
|
374
|
+
for (const line of lines) {
|
|
375
|
+
try {
|
|
376
|
+
events.push(JSON.parse(line));
|
|
377
|
+
}
|
|
378
|
+
catch {
|
|
379
|
+
log.warn({ line }, 'Skipping malformed hook event log line');
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
if (events.length === 0) {
|
|
383
|
+
await node_fs.writeFile(logPath, '', 'utf-8');
|
|
384
|
+
return 0;
|
|
385
|
+
}
|
|
386
|
+
try {
|
|
387
|
+
// Use native fetch (Node 18+ — required minimum for this monorepo)
|
|
388
|
+
const response = await fetch(`${controlPlaneUrl}/api/v1/hook-events`, {
|
|
389
|
+
method: 'POST',
|
|
390
|
+
headers: {
|
|
391
|
+
'Content-Type': 'application/json',
|
|
392
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
393
|
+
},
|
|
394
|
+
body: JSON.stringify({ events }),
|
|
395
|
+
signal: AbortSignal.timeout(10_000),
|
|
396
|
+
});
|
|
397
|
+
if (response.ok) {
|
|
398
|
+
// Truncate the log — events successfully delivered
|
|
399
|
+
await node_fs.writeFile(logPath, '', 'utf-8');
|
|
400
|
+
log.info({ count: events.length }, 'Hook events drained to control plane');
|
|
401
|
+
return events.length;
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
log.warn({ status: response.status, count: events.length }, 'Control plane rejected hook events — retaining log for next cycle');
|
|
405
|
+
return 0;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
catch (err) {
|
|
409
|
+
// Network failure — retain log for retry on next cycle
|
|
410
|
+
log.warn({ err, count: events.length }, 'Failed to drain hook events — will retry on next sync cycle');
|
|
411
|
+
return 0;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
//# sourceMappingURL=claudeCodeHooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claudeCodeHooks.js","sourceRoot":"","sources":["../../src/harness/claudeCodeHooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* claudeDesktopHooks.ts — Claude Desktop PreToolUse hook generator.
|
|
3
|
+
*
|
|
4
|
+
* Claude Desktop shares the same hook format as Claude Code (PreToolUse,
|
|
5
|
+
* exit 2 on stdin JSON) but reads its config from an OS-specific location:
|
|
6
|
+
* macOS : ~/Library/Application Support/Claude/claude_desktop_config.json
|
|
7
|
+
* Linux : ~/.config/Claude/claude_desktop_config.json
|
|
8
|
+
*
|
|
9
|
+
* Hook events are written via dual-path logEvent:
|
|
10
|
+
* Path B (reliable) — synchronous append to .intutic/events/claude-desktop-hook-events.jsonl
|
|
11
|
+
* Path A (telemetry) — fire-and-forget https.request to the control plane
|
|
12
|
+
*
|
|
13
|
+
* LLD #14 — Phase 3 cross-harness defence (Gap 3, WS-B)
|
|
14
|
+
* HLD §3.14 — Three-Tier Defense Cascade (Tier 1 Native Gating)
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Write the Claude Desktop governance hook script and merge hook configuration
|
|
20
|
+
* into the OS-specific claude_desktop_config.json.
|
|
21
|
+
*
|
|
22
|
+
* Safe to call repeatedly — uses atomic rename (`.intutic-tmp` → final path).
|
|
23
|
+
* Does NOT touch the `mcpServers` section of the config.
|
|
24
|
+
*
|
|
25
|
+
* @param workspaceRoot - Absolute workspace root path.
|
|
26
|
+
* @param proxyUrl - Intutic proxy URL written into the hook script header.
|
|
27
|
+
* @param workspaceId - Workspace ID embedded in every hook event payload.
|
|
28
|
+
*/
|
|
29
|
+
export declare function writeClaudeDesktopHooks(workspaceRoot: string, proxyUrl: string, workspaceId?: string): Promise<void>;
|
|
30
|
+
//# sourceMappingURL=claudeDesktopHooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claudeDesktopHooks.d.ts","sourceRoot":"","sources":["../../src/harness/claudeDesktopHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAqMH;;;;;;;;;;GAUG;AACH,wBAAsB,uBAAuB,CAC3C,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,GACf,OAAO,CAAC,IAAI,CAAC,CAsEf"}
|