@intutic/sync-daemon 1.9.0 → 1.10.1
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 +2 -2
- package/dist/agentReporter.d.ts +205 -0
- package/dist/agentReporter.d.ts.map +1 -0
- package/dist/agentReporter.js +394 -0
- package/dist/agentReporter.js.map +1 -0
- package/dist/configReader.d.ts +39 -2
- package/dist/configReader.d.ts.map +1 -1
- package/dist/configReader.js +56 -2
- package/dist/configReader.js.map +1 -1
- package/dist/configWriter.d.ts +51 -1
- package/dist/configWriter.d.ts.map +1 -1
- package/dist/configWriter.js +490 -63
- package/dist/configWriter.js.map +1 -1
- package/dist/harness/antigravityHooks.d.ts.map +1 -1
- package/dist/harness/antigravityHooks.js +33 -55
- package/dist/harness/antigravityHooks.js.map +1 -1
- package/dist/harness/claudeCodeHooks.d.ts +47 -13
- package/dist/harness/claudeCodeHooks.d.ts.map +1 -1
- package/dist/harness/claudeCodeHooks.js +323 -55
- package/dist/harness/claudeCodeHooks.js.map +1 -1
- package/dist/harness/claudeDesktopHooks.d.ts.map +1 -1
- package/dist/harness/claudeDesktopHooks.js +35 -48
- package/dist/harness/claudeDesktopHooks.js.map +1 -1
- package/dist/harness/clineHooks.d.ts.map +1 -1
- package/dist/harness/clineHooks.js +42 -34
- package/dist/harness/clineHooks.js.map +1 -1
- package/dist/harness/codexHooks.d.ts +36 -0
- package/dist/harness/codexHooks.d.ts.map +1 -0
- package/dist/harness/codexHooks.js +227 -0
- package/dist/harness/codexHooks.js.map +1 -0
- package/dist/harness/continueHooks.d.ts +45 -5
- package/dist/harness/continueHooks.d.ts.map +1 -1
- package/dist/harness/continueHooks.js +225 -5
- package/dist/harness/continueHooks.js.map +1 -1
- package/dist/harness/cursorHooks.d.ts +31 -0
- package/dist/harness/cursorHooks.d.ts.map +1 -1
- package/dist/harness/cursorHooks.js +78 -49
- package/dist/harness/cursorHooks.js.map +1 -1
- package/dist/harness/dshHooks.d.ts +198 -0
- package/dist/harness/dshHooks.d.ts.map +1 -0
- package/dist/harness/dshHooks.js +569 -0
- package/dist/harness/dshHooks.js.map +1 -0
- package/dist/harness/gateBody.d.ts +422 -0
- package/dist/harness/gateBody.d.ts.map +1 -0
- package/dist/harness/gateBody.js +1816 -0
- package/dist/harness/gateBody.js.map +1 -0
- package/dist/harness/gateKind.d.ts +164 -0
- package/dist/harness/gateKind.d.ts.map +1 -0
- package/dist/harness/gateKind.js +143 -0
- package/dist/harness/gateKind.js.map +1 -0
- package/dist/harness/githubCopilotHooks.d.ts +54 -0
- package/dist/harness/githubCopilotHooks.d.ts.map +1 -0
- package/dist/harness/githubCopilotHooks.js +218 -0
- package/dist/harness/githubCopilotHooks.js.map +1 -0
- package/dist/harness/gooseHooks.d.ts.map +1 -1
- package/dist/harness/gooseHooks.js +67 -37
- package/dist/harness/gooseHooks.js.map +1 -1
- package/dist/harness/grokHooks.d.ts +77 -0
- package/dist/harness/grokHooks.d.ts.map +1 -0
- package/dist/harness/grokHooks.js +350 -0
- package/dist/harness/grokHooks.js.map +1 -0
- package/dist/harness/hermesHooks.d.ts.map +1 -1
- package/dist/harness/hermesHooks.js +26 -55
- package/dist/harness/hermesHooks.js.map +1 -1
- package/dist/harness/holdRedaction.d.ts +69 -0
- package/dist/harness/holdRedaction.d.ts.map +1 -0
- package/dist/harness/holdRedaction.js +211 -0
- package/dist/harness/holdRedaction.js.map +1 -0
- package/dist/harness/jetbrainsXmlConfig.d.ts +51 -0
- package/dist/harness/jetbrainsXmlConfig.d.ts.map +1 -0
- package/dist/harness/jetbrainsXmlConfig.js +151 -0
- package/dist/harness/jetbrainsXmlConfig.js.map +1 -0
- package/dist/harness/mcpAutoWrite.d.ts +100 -3
- package/dist/harness/mcpAutoWrite.d.ts.map +1 -1
- package/dist/harness/mcpAutoWrite.js +712 -90
- package/dist/harness/mcpAutoWrite.js.map +1 -1
- package/dist/harness/museHooks.d.ts +53 -0
- package/dist/harness/museHooks.d.ts.map +1 -0
- package/dist/harness/museHooks.js +273 -0
- package/dist/harness/museHooks.js.map +1 -0
- package/dist/harness/n8nHooks.d.ts +25 -9
- package/dist/harness/n8nHooks.d.ts.map +1 -1
- package/dist/harness/n8nHooks.js +174 -9
- package/dist/harness/n8nHooks.js.map +1 -1
- package/dist/harness/openCodeHooks.d.ts +93 -0
- package/dist/harness/openCodeHooks.d.ts.map +1 -0
- package/dist/harness/openCodeHooks.js +248 -0
- package/dist/harness/openCodeHooks.js.map +1 -0
- package/dist/harness/openWebuiHooks.d.ts.map +1 -1
- package/dist/harness/openWebuiHooks.js +56 -11
- package/dist/harness/openWebuiHooks.js.map +1 -1
- package/dist/harness/openclawHooks.d.ts.map +1 -1
- package/dist/harness/openclawHooks.js +30 -54
- package/dist/harness/openclawHooks.js.map +1 -1
- package/dist/harness/openhandsHooks.d.ts.map +1 -1
- package/dist/harness/openhandsHooks.js +35 -41
- package/dist/harness/openhandsHooks.js.map +1 -1
- package/dist/harness/piHooks.d.ts.map +1 -1
- package/dist/harness/piHooks.js +31 -58
- package/dist/harness/piHooks.js.map +1 -1
- package/dist/harness/protectedPaths.d.ts +353 -0
- package/dist/harness/protectedPaths.d.ts.map +1 -0
- package/dist/harness/protectedPaths.js +805 -0
- package/dist/harness/protectedPaths.js.map +1 -0
- package/dist/harness/rooCodeHooks.d.ts.map +1 -1
- package/dist/harness/rooCodeHooks.js +34 -48
- package/dist/harness/rooCodeHooks.js.map +1 -1
- package/dist/harness/windsurfHooks.d.ts +72 -0
- package/dist/harness/windsurfHooks.d.ts.map +1 -1
- package/dist/harness/windsurfHooks.js +172 -58
- package/dist/harness/windsurfHooks.js.map +1 -1
- package/dist/harness/windsurfJetBrainsProxy.d.ts +78 -0
- package/dist/harness/windsurfJetBrainsProxy.d.ts.map +1 -0
- package/dist/harness/windsurfJetBrainsProxy.js +159 -0
- package/dist/harness/windsurfJetBrainsProxy.js.map +1 -0
- package/dist/hashReporter.js +1 -1
- package/dist/hashReporter.js.map +1 -1
- package/dist/index.d.ts +12 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -6
- package/dist/index.js.map +1 -1
- package/dist/integrityStore.d.ts +0 -8
- package/dist/integrityStore.d.ts.map +1 -1
- package/dist/integrityStore.js +6 -29
- package/dist/integrityStore.js.map +1 -1
- package/dist/lib/approvedBypasses.d.ts +76 -0
- package/dist/lib/approvedBypasses.d.ts.map +1 -0
- package/dist/lib/approvedBypasses.js +140 -0
- package/dist/lib/approvedBypasses.js.map +1 -0
- package/dist/lib/complianceProbes.d.ts.map +1 -1
- package/dist/lib/complianceProbes.js +5 -0
- package/dist/lib/complianceProbes.js.map +1 -1
- package/dist/lib/decisionsDigest.d.ts +99 -0
- package/dist/lib/decisionsDigest.d.ts.map +1 -0
- package/dist/lib/decisionsDigest.js +199 -0
- package/dist/lib/decisionsDigest.js.map +1 -0
- package/dist/lib/egressPolicy.d.ts +69 -0
- package/dist/lib/egressPolicy.d.ts.map +1 -0
- package/dist/lib/egressPolicy.js +126 -0
- package/dist/lib/egressPolicy.js.map +1 -0
- package/dist/lib/gitWorktrees.d.ts +140 -0
- package/dist/lib/gitWorktrees.d.ts.map +1 -0
- package/dist/lib/gitWorktrees.js +191 -0
- package/dist/lib/gitWorktrees.js.map +1 -0
- package/dist/lib/policySnapshot.d.ts +224 -0
- package/dist/lib/policySnapshot.d.ts.map +1 -0
- package/dist/lib/policySnapshot.js +637 -0
- package/dist/lib/policySnapshot.js.map +1 -0
- package/dist/lib/processPoller.d.ts +44 -0
- package/dist/lib/processPoller.d.ts.map +1 -1
- package/dist/lib/processPoller.js +176 -1
- package/dist/lib/processPoller.js.map +1 -1
- package/dist/lib/runtimeEnv.d.ts +8 -0
- package/dist/lib/runtimeEnv.d.ts.map +1 -1
- package/dist/lib/runtimeEnv.js +3 -0
- package/dist/lib/runtimeEnv.js.map +1 -1
- package/dist/localTrajectoryHeuristics.d.ts +51 -0
- package/dist/localTrajectoryHeuristics.d.ts.map +1 -0
- package/dist/localTrajectoryHeuristics.js +90 -0
- package/dist/localTrajectoryHeuristics.js.map +1 -0
- package/dist/sessionReporter.d.ts +70 -0
- package/dist/sessionReporter.d.ts.map +1 -0
- package/dist/sessionReporter.js +140 -0
- package/dist/sessionReporter.js.map +1 -0
- package/dist/skillWriter.d.ts +1 -1
- package/dist/skillWriter.d.ts.map +1 -1
- package/dist/skillWriter.js +6 -3
- package/dist/skillWriter.js.map +1 -1
- package/dist/syncLoop.d.ts +105 -3
- package/dist/syncLoop.d.ts.map +1 -1
- package/dist/syncLoop.js +397 -65
- package/dist/syncLoop.js.map +1 -1
- package/dist/trajectoryMonitor.d.ts +2 -0
- package/dist/trajectoryMonitor.d.ts.map +1 -1
- package/dist/trajectoryMonitor.js +57 -0
- package/dist/trajectoryMonitor.js.map +1 -1
- package/dist/watcher/driftWatcher.d.ts.map +1 -1
- package/dist/watcher/driftWatcher.js +12 -3
- package/dist/watcher/driftWatcher.js.map +1 -1
- package/dist/watcher/settingsGuard.d.ts +23 -0
- package/dist/watcher/settingsGuard.d.ts.map +1 -1
- package/dist/watcher/settingsGuard.js +202 -17
- package/dist/watcher/settingsGuard.js.map +1 -1
- package/package.json +37 -6
- package/dist/brainIndexer.d.ts +0 -51
- package/dist/brainIndexer.d.ts.map +0 -1
- package/dist/brainIndexer.js +0 -148
- package/dist/brainIndexer.js.map +0 -1
- package/dist/harness/cursorHooksJson.d.ts +0 -29
- package/dist/harness/cursorHooksJson.d.ts.map +0 -1
- package/dist/harness/cursorHooksJson.js +0 -130
- package/dist/harness/cursorHooksJson.js.map +0 -1
- package/dist/harness/gooseHooksWriter.d.ts +0 -27
- package/dist/harness/gooseHooksWriter.d.ts.map +0 -1
- package/dist/harness/gooseHooksWriter.js +0 -147
- package/dist/harness/gooseHooksWriter.js.map +0 -1
- package/dist/harness/openhandsHooksWriter.d.ts +0 -23
- package/dist/harness/openhandsHooksWriter.d.ts.map +0 -1
- package/dist/harness/openhandsHooksWriter.js +0 -146
- package/dist/harness/openhandsHooksWriter.js.map +0 -1
|
@@ -20,6 +20,7 @@ import * as path from 'node:path';
|
|
|
20
20
|
import * as os from 'node:os';
|
|
21
21
|
import { createLogger } from '@intutic/logger';
|
|
22
22
|
import { newIso } from '@intutic/id';
|
|
23
|
+
import { emitPythonGate } from './gateBody.js';
|
|
23
24
|
const log = createLogger('sync-open-webui-hooks');
|
|
24
25
|
/** Default target directory for the Open WebUI governance filter. */
|
|
25
26
|
const OPEN_WEBUI_DIR = path.join(os.homedir(), '.open-webui');
|
|
@@ -33,6 +34,7 @@ Proxy: ${proxyUrl}
|
|
|
33
34
|
Workspace: ${workspaceId}
|
|
34
35
|
"""
|
|
35
36
|
import os
|
|
37
|
+
import re
|
|
36
38
|
import json
|
|
37
39
|
import urllib.request
|
|
38
40
|
import hashlib
|
|
@@ -92,28 +94,71 @@ def _log_event(env: dict, event: dict):
|
|
|
92
94
|
pass
|
|
93
95
|
|
|
94
96
|
|
|
97
|
+
${emitPythonGate()}
|
|
98
|
+
|
|
99
|
+
|
|
95
100
|
class Filter:
|
|
96
101
|
"""Intutic governance inlet filter for Open WebUI."""
|
|
97
102
|
|
|
98
103
|
def inlet(self, body: dict, __user__: Optional[dict] = None) -> dict:
|
|
99
|
-
"""Called before every prompt
|
|
104
|
+
"""Called before every prompt.
|
|
105
|
+
|
|
106
|
+
This used to hardcode "tool_allowed" and return the body unconditionally
|
|
107
|
+
— it recorded, it could not refuse, and nothing distinguished that from a
|
|
108
|
+
gate that had simply never blocked anything. It can refuse now; raising
|
|
109
|
+
is how an Open WebUI filter does so.
|
|
110
|
+
|
|
111
|
+
What it refuses is narrower than the other harnesses on purpose. A prompt
|
|
112
|
+
is not a tool call: there is no path argument to guard and no command to
|
|
113
|
+
run. Text matching a protected-path or bypass rule is someone *asking
|
|
114
|
+
about* a file, so those flag rather than block. Only a policy-snapshot
|
|
115
|
+
rule whose severity is "block" stops a prompt.
|
|
116
|
+
"""
|
|
100
117
|
env = _load_runtime_env()
|
|
101
118
|
ts = datetime.now(timezone.utc).isoformat()
|
|
102
119
|
model = body.get("model", "unknown")
|
|
103
120
|
workspace_id = env["INTUTIC_WORKSPACE_ID"]
|
|
121
|
+
meta = body.get("metadata") or {}
|
|
122
|
+
session_id = meta.get("session_id") or meta.get("chat_id") or ""
|
|
104
123
|
incident_id = hashlib.sha1(
|
|
105
124
|
f"{ts}{model}{workspace_id}".encode()
|
|
106
125
|
).hexdigest()[:16]
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
"
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
126
|
+
|
|
127
|
+
# Concatenate the user turns only. System and assistant text is ours and
|
|
128
|
+
# the model's, not the governed party's.
|
|
129
|
+
prompt = " ".join(
|
|
130
|
+
str(m.get("content", ""))
|
|
131
|
+
for m in (body.get("messages") or [])
|
|
132
|
+
if isinstance(m, dict) and m.get("role") == "user"
|
|
133
|
+
)
|
|
134
|
+
blocks, flags, shadowed = _intutic_evaluate(prompt)
|
|
135
|
+
|
|
136
|
+
def emit(verdict, reason):
|
|
137
|
+
_log_event(env, {
|
|
138
|
+
"event": verdict,
|
|
139
|
+
"toolName": f"open-webui:{model}",
|
|
140
|
+
"reason": reason,
|
|
141
|
+
"workspaceId": workspace_id,
|
|
142
|
+
"harnessType": HARNESS_TYPE,
|
|
143
|
+
"timestamp": ts,
|
|
144
|
+
"incidentId": incident_id,
|
|
145
|
+
**({"sessionId": session_id} if session_id else {}),
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
for rid, reason in flags:
|
|
149
|
+
emit("tool_flagged", f"{reason} [{rid}]")
|
|
150
|
+
|
|
151
|
+
# Certain rules the workspace asked us not to act on. Reported apart
|
|
152
|
+
# from flags so a SHADOW rollout is measurable.
|
|
153
|
+
for rid, reason in shadowed:
|
|
154
|
+
emit("tool_would_block", f"{reason} [{rid}]")
|
|
155
|
+
|
|
156
|
+
if blocks:
|
|
157
|
+
rid, reason = blocks[0]
|
|
158
|
+
emit("tool_blocked", f"{reason} [{rid}]")
|
|
159
|
+
raise Exception(f"[Intutic Governance] BLOCKED: {reason} [{rid}]")
|
|
160
|
+
|
|
161
|
+
emit("tool_allowed", "")
|
|
117
162
|
return body
|
|
118
163
|
|
|
119
164
|
def outlet(self, body: dict, __user__: Optional[dict] = None) -> dict:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openWebuiHooks.js","sourceRoot":"","sources":["../../src/harness/openWebuiHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACtC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAA;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"openWebuiHooks.js","sourceRoot":"","sources":["../../src/harness/openWebuiHooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openclawHooks.d.ts","sourceRoot":"","sources":["../../src/harness/openclawHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;
|
|
1
|
+
{"version":3,"file":"openclawHooks.d.ts","sourceRoot":"","sources":["../../src/harness/openclawHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AA2MH;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CACtC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,GACf,OAAO,CAAC,IAAI,CAAC,CA+Ef"}
|
|
@@ -26,34 +26,24 @@ import { exec } from 'node:child_process';
|
|
|
26
26
|
import { promisify } from 'node:util';
|
|
27
27
|
import { createLogger } from '@intutic/logger';
|
|
28
28
|
import { newIso } from '@intutic/id';
|
|
29
|
+
import { emitJsGate, emitJsFailClosedPrelude } from './gateBody.js';
|
|
29
30
|
const log = createLogger('sync-openclaw-hooks');
|
|
30
31
|
const execAsync = promisify(exec);
|
|
31
32
|
/** Path to the Openclaw global config file. */
|
|
32
33
|
const OPENCLAW_CONFIG = path.join(os.homedir(), '.openclaw', 'openclaw.json');
|
|
33
34
|
/** Governance-sensitive paths that the hook gate protects. */
|
|
34
|
-
const PROTECTED_PATHS = [
|
|
35
|
-
'.gemini/settings.json',
|
|
36
|
-
'.intutic/hooks',
|
|
37
|
-
'.intutic/integrity.json',
|
|
38
|
-
'.intutic/events',
|
|
39
|
-
'.claude/settings.json',
|
|
40
|
-
'.cursor/hooks.json',
|
|
41
|
-
'.codeium/windsurf/hooks.json',
|
|
42
|
-
'.cline/hooks',
|
|
43
|
-
'.agents/plugins/intutic-governance',
|
|
44
|
-
'.openhands/hooks.json',
|
|
45
|
-
'.openclaw/openclaw.json',
|
|
46
|
-
];
|
|
47
35
|
// ─── Node.js hook script template ────────────────────────────────────────────
|
|
48
|
-
function buildOpenclawCheckScript(hookEventsLog, workspaceId) {
|
|
36
|
+
function buildOpenclawCheckScript(hookEventsLog, workspaceId, proxyUrl) {
|
|
49
37
|
return `#!/usr/bin/env node
|
|
50
38
|
/**
|
|
51
39
|
* Intutic Openclaw PreToolUse execution gate.
|
|
52
40
|
* Auto-generated by intutic sync-daemon. DO NOT EDIT.
|
|
41
|
+
* Proxy: ${proxyUrl}
|
|
53
42
|
* Generated: ${newIso()}
|
|
54
43
|
* Workspace: ${workspaceId}
|
|
55
44
|
*/
|
|
56
45
|
'use strict';
|
|
46
|
+
${emitJsFailClosedPrelude({ harness: 'openclaw', contract: 'exit2' })}
|
|
57
47
|
const path = require('path');
|
|
58
48
|
const os = require('os');
|
|
59
49
|
const fs = require('fs');
|
|
@@ -73,26 +63,29 @@ try {
|
|
|
73
63
|
});
|
|
74
64
|
} catch { /* runtime.env not yet written — use defaults */ }
|
|
75
65
|
|
|
76
|
-
|
|
77
|
-
const PROTECTED_PATHS = ${JSON.stringify(PROTECTED_PATHS, null, 2)};
|
|
66
|
+
${emitJsGate({ harness: 'openclaw', contract: 'exit2' })}
|
|
78
67
|
|
|
79
68
|
/**
|
|
80
69
|
* Appends a governance event to the local hook-events log (Path B)
|
|
81
70
|
* and fires a non-blocking HTTP POST to the control plane (Path A).
|
|
82
71
|
*/
|
|
72
|
+
let _intuticSessionId = '';
|
|
83
73
|
function logEvent(verdict, toolName, reason) {
|
|
84
74
|
try {
|
|
85
75
|
const ts = new Date().toISOString();
|
|
86
76
|
// incidentId = sha1(timestamp + toolName + workspaceId).slice(0,16)
|
|
87
77
|
const incidentId = crypto.createHash('sha1').update(ts + toolName + _intuticWsId).digest('hex').slice(0, 16);
|
|
88
78
|
const entry = JSON.stringify({
|
|
89
|
-
|
|
79
|
+
// Passed through, not collapsed to two values: the advisory tier emits
|
|
80
|
+
// 'tool_flagged', and a ternary here silently recorded it as an allow.
|
|
81
|
+
event: verdict,
|
|
90
82
|
toolName,
|
|
91
83
|
reason: reason || '',
|
|
92
84
|
workspaceId: _intuticWsId,
|
|
93
85
|
harnessType: 'openclaw',
|
|
94
86
|
timestamp: ts,
|
|
95
87
|
incidentId,
|
|
88
|
+
...(_intuticSessionId ? { sessionId: _intuticSessionId } : {}),
|
|
96
89
|
}) + '\\n';
|
|
97
90
|
// Path B: reliable file append (sync-daemon drains on FSEvents change)
|
|
98
91
|
fs.appendFileSync(${JSON.stringify(hookEventsLog)}, entry, { flag: 'a' });
|
|
@@ -128,46 +121,30 @@ process.stdin.on('data', (chunk) => { inputData += chunk; });
|
|
|
128
121
|
process.stdin.on('end', () => {
|
|
129
122
|
try {
|
|
130
123
|
const ctx = JSON.parse(inputData);
|
|
131
|
-
|
|
124
|
+
_intuticSessionId = ctx.session_id || ctx.sessionId || ctx.conversation_id || ctx.conversationId || ctx.task_id || ctx.taskId || '';
|
|
125
|
+
// An envelope carrying none of the tool fields extracts to empty strings,
|
|
126
|
+
// which match no rule — an allow. Refused instead; see the guard itself.
|
|
127
|
+
intuticGuardEnvelope(ctx, ['tool_name', 'toolName', 'tool_input', 'toolInput'], logEvent);
|
|
128
|
+
const toolName = (ctx.tool_name || ctx.toolName || '').toLowerCase()
|
|
129
|
+
// Case preserved for the gate. A BLOCK: SOP compiles to a tool-name
|
|
130
|
+
// pattern the operator wrote as they see it (Bash, Write) and this
|
|
131
|
+
// harness lowercases the tool for its own matching. Handing the gate the
|
|
132
|
+
// lowercased form means every SOP tool rule silently matches nothing here
|
|
133
|
+
// while appearing active everywhere else.
|
|
134
|
+
const rawToolName = ctx.tool_name || ctx.toolName || '';
|
|
132
135
|
const toolInput = ctx.tool_input || ctx.toolInput || {};
|
|
133
136
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const reason = \`Attempt to modify governance-protected path: "\${targetPath}". \` +
|
|
139
|
-
'To change governance policy, update your SOP via the Intutic control plane.';
|
|
140
|
-
console.error(\`[Intutic Governance] BLOCKED: \${reason}\`);
|
|
141
|
-
logEvent('blocked', toolName, reason);
|
|
142
|
-
process.exit(2);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// 2. Bash/shell command guard — block governance bypass patterns
|
|
147
|
-
const command = toolInput.command || toolInput.cmd || '';
|
|
148
|
-
const BYPASS_PATTERNS = [
|
|
149
|
-
'chflags nouchg',
|
|
150
|
-
'chattr -i',
|
|
151
|
-
'rm -rf .intutic',
|
|
152
|
-
'rm -rf .openclaw',
|
|
153
|
-
'OPENCLAW_PRE_TOOL_HOOK=',
|
|
154
|
-
'rm -rf .gemini/settings',
|
|
155
|
-
];
|
|
156
|
-
for (const pattern of BYPASS_PATTERNS) {
|
|
157
|
-
if (String(command).includes(pattern)) {
|
|
158
|
-
const reason = \`Shell command matches governance bypass pattern: "\${pattern}"\`;
|
|
159
|
-
console.error(\`[Intutic Governance] BLOCKED: \${reason}\`);
|
|
160
|
-
logEvent('blocked', toolName, reason);
|
|
161
|
-
process.exit(2);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
137
|
+
const targetPath = toolInput.path || toolInput.file_path || toolInput.filePath ||
|
|
138
|
+
toolInput.new_path || toolInput.target || toolInput.notebook_path || '';
|
|
139
|
+
const command = toolInput.command || toolInput.cmd || toolInput.script || '';
|
|
140
|
+
intuticGate(rawToolName, targetPath, command, logEvent, _intuticWsId, toolInput);
|
|
164
141
|
|
|
165
|
-
logEvent('
|
|
142
|
+
logEvent('tool_allowed', toolName, '');
|
|
166
143
|
process.exit(0);
|
|
167
144
|
} catch (err) {
|
|
168
145
|
// Fail CLOSED — any hook execution error blocks the tool call.
|
|
169
146
|
console.error('[Intutic Governance] Hook error (blocking for safety):', err);
|
|
170
|
-
logEvent('
|
|
147
|
+
logEvent('tool_blocked', 'unknown', String(err));
|
|
171
148
|
process.exit(2);
|
|
172
149
|
}
|
|
173
150
|
});
|
|
@@ -252,7 +229,7 @@ export async function writeOpenclawHooks(workspaceRoot, proxyUrl, workspaceId =
|
|
|
252
229
|
const hookScriptPath = path.join(globalHookDir, 'openclaw-check.js');
|
|
253
230
|
const hookEventsLog = path.join(hookEventsDir, 'hook-events.jsonl');
|
|
254
231
|
const tmpScript = hookScriptPath + '.intutic-tmp';
|
|
255
|
-
await fs.writeFile(tmpScript, buildOpenclawCheckScript(hookEventsLog, workspaceId), 'utf-8');
|
|
232
|
+
await fs.writeFile(tmpScript, buildOpenclawCheckScript(hookEventsLog, workspaceId, proxyUrl), 'utf-8');
|
|
256
233
|
await fs.rename(tmpScript, hookScriptPath);
|
|
257
234
|
await fs.chmod(hookScriptPath, 0o755);
|
|
258
235
|
log.info({ action: 'openclaw_hook_script_written', path: hookScriptPath }, 'Openclaw hook script written');
|
|
@@ -273,8 +250,8 @@ export async function writeOpenclawHooks(workspaceRoot, proxyUrl, workspaceId =
|
|
|
273
250
|
// ── 4. Write env snippet ───────────────────────────────────────────────────
|
|
274
251
|
const envSnippet = [
|
|
275
252
|
`# Intutic openclaw governance env`,
|
|
276
|
-
`
|
|
277
|
-
`
|
|
253
|
+
`INTUTIC_OPENCLAW_HOOK=~/.intutic/hooks/openclaw-check.js`,
|
|
254
|
+
`INTUTIC_OPENCLAW_CONFIG=~/.openclaw/openclaw.json`,
|
|
278
255
|
`# Validate: openclaw hooks check`,
|
|
279
256
|
].join('\n') + '\n';
|
|
280
257
|
const envFilePath = path.join(envDir, 'openclaw.env');
|
|
@@ -291,6 +268,5 @@ export async function writeOpenclawHooks(workspaceRoot, proxyUrl, workspaceId =
|
|
|
291
268
|
// Not installed or check failed — non-fatal
|
|
292
269
|
log.debug({ action: 'openclaw_hooks_check_skipped', err }, 'openclaw hooks check skipped (binary not in PATH or check failed)');
|
|
293
270
|
}
|
|
294
|
-
void proxyUrl; // proxyUrl captured in hook script header via newIso comment
|
|
295
271
|
}
|
|
296
272
|
//# sourceMappingURL=openclawHooks.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openclawHooks.js","sourceRoot":"","sources":["../../src/harness/openclawHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACtC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAA;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"openclawHooks.js","sourceRoot":"","sources":["../../src/harness/openclawHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACtC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAA;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,UAAU,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AAEnE,MAAM,GAAG,GAAG,YAAY,CAAC,qBAAqB,CAAC,CAAA;AAC/C,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;AAEjC,+CAA+C;AAC/C,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,eAAe,CAAC,CAAA;AAE7E,8DAA8D;AAE9D,gFAAgF;AAEhF,SAAS,wBAAwB,CAC/B,aAAqB,EACrB,WAAmB,EACnB,QAAgB;IAEhB,OAAO;;;;YAIG,QAAQ;gBACJ,MAAM,EAAE;gBACR,WAAW;;;EAGzB,uBAAuB,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;;;;;;;;;gFASW,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC;;;;;;;;;;;EAWzG,UAAU,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;wBAyBhC,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4DpD,CAAA;AACD,CAAC;AAED,iFAAiF;AAEjF;;;GAGG;AACH,SAAS,cAAc,CAAC,GAAW;IACjC,0BAA0B;IAC1B,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAA;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;IAED,2CAA2C;IAC3C,IAAI,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAA;IAC7C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAA4B,CAAA;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;IAED,sDAAsD;IACtD,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,cAAc,EAAE,IAAI,CAAC,CAAA;IACjD,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAA4B,CAAA;AACxD,CAAC;AAED;;;GAGG;AACH,SAAS,mBAAmB,CAC1B,QAAiC,EACjC,cAAsB;IAEtB,MAAM,KAAK,GAAI,QAAQ,CAAC,KAA6C,IAAI,EAAE,CAAA;IAC3E,MAAM,QAAQ,GAAI,KAAK,CAAC,QAAgD,IAAI,EAAE,CAAA;IAC9E,MAAM,OAAO,GAAI,QAAQ,CAAC,OAA+C,IAAI,EAAE,CAAA;IAE/E,OAAO;QACL,GAAG,QAAQ;QACX,KAAK,EAAE;YACL,GAAG,KAAK;YACR,QAAQ,EAAE;gBACR,GAAG,QAAQ;gBACX,OAAO,EAAE;oBACP,GAAG,OAAO;oBACV,oBAAoB,EAAE;wBACpB,OAAO,EAAE,IAAI;wBACb,KAAK,EAAE,YAAY;wBACnB,OAAO,EAAE,cAAc;qBACxB;iBACF;aACF;SACF;KACF,CAAA;AACH,CAAC;AAED,iFAAiF;AAEjF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,aAAqB,EACrB,QAAgB,EAChB,WAAW,GAAG,EAAE;IAEhB,8EAA8E;IAE9E,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,CAAA;IAClE,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAA;IACpE,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,UAAU,EAAE,KAAK,CAAC,CAAA;IAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,CAAA;IAExD,MAAM,OAAO,CAAC,GAAG,CAAC;QAChB,EAAE,CAAC,KAAK,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QAC5C,EAAE,CAAC,KAAK,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QAC5C,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QACrC,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;KAC3C,CAAC,CAAA;IAEF,6EAA6E;IAE7E,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,mBAAmB,CAAC,CAAA;IACpE,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,mBAAmB,CAAC,CAAA;IAEnE,MAAM,SAAS,GAAG,cAAc,GAAG,cAAc,CAAA;IACjD,MAAM,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,wBAAwB,CAAC,aAAa,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAA;IACtG,MAAM,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,cAAc,CAAC,CAAA;IAC1C,MAAM,EAAE,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAA;IAErC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,8BAA8B,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,8BAA8B,CAAC,CAAA;IAE1G,6EAA6E;IAE7E,IAAI,cAAc,GAA4B,EAAE,CAAA;IAChD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC,CAAA;QACvD,cAAc,GAAG,cAAc,CAAC,GAAG,CAAC,CAAA;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,qDAAqD;IACvD,CAAC;IAED,MAAM,YAAY,GAAG,mBAAmB,CAAC,cAAc,EAAE,cAAc,CAAC,CAAA;IAExE,MAAM,SAAS,GAAG,eAAe,GAAG,cAAc,CAAA;IAClD,MAAM,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAA;IACpF,MAAM,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,eAAe,CAAC,CAAA;IAE3C,GAAG,CAAC,IAAI,CACN,EAAE,MAAM,EAAE,wBAAwB,EAAE,IAAI,EAAE,eAAe,EAAE,EAC3D,oDAAoD,CACrD,CAAA;IAED,8EAA8E;IAE9E,MAAM,UAAU,GAAG;QACjB,mCAAmC;QACnC,0DAA0D;QAC1D,mDAAmD;QACnD,kCAAkC;KACnC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IAEnB,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,CAAA;IACrD,MAAM,MAAM,GAAG,WAAW,GAAG,cAAc,CAAA;IAC3C,MAAM,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,CAAA;IAC/C,MAAM,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;IAEpC,GAAG,CAAC,IAAI,CACN,EAAE,MAAM,EAAE,sBAAsB,EAAE,IAAI,EAAE,WAAW,EAAE,EACrD,8BAA8B,CAC/B,CAAA;IAED,6EAA6E;IAE7E,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,sBAAsB,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;QACrF,GAAG,CAAC,IAAI,CACN,EAAE,MAAM,EAAE,sBAAsB,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,EAChF,6BAA6B,CAC9B,CAAA;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,4CAA4C;QAC5C,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,8BAA8B,EAAE,GAAG,EAAE,EAAE,mEAAmE,CAAC,CAAA;IACjI,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openhandsHooks.d.ts","sourceRoot":"","sources":["../../src/harness/openhandsHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;
|
|
1
|
+
{"version":3,"file":"openhandsHooks.d.ts","sourceRoot":"","sources":["../../src/harness/openhandsHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAUH;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,GACf,OAAO,CAAC,IAAI,CAAC,CA+Gf"}
|
|
@@ -16,17 +16,8 @@ import * as fs from 'node:fs/promises';
|
|
|
16
16
|
import * as path from 'node:path';
|
|
17
17
|
import { createLogger } from '@intutic/logger';
|
|
18
18
|
import { newIso } from '@intutic/id';
|
|
19
|
+
import { emitShellGate, SHELL_EXTRACT, SHELL_FAIL_CLOSED } from './gateBody.js';
|
|
19
20
|
const log = createLogger('sync-openhands-hooks');
|
|
20
|
-
const PROTECTED_PATHS = [
|
|
21
|
-
'.openhands/hooks.json',
|
|
22
|
-
'.intutic/hooks',
|
|
23
|
-
'.intutic/integrity.json',
|
|
24
|
-
'.claude/settings.json',
|
|
25
|
-
'.cursor/hooks.json',
|
|
26
|
-
'.codeium/windsurf/hooks.json',
|
|
27
|
-
'.cline/hooks',
|
|
28
|
-
'.agents/plugins/intutic-governance',
|
|
29
|
-
];
|
|
30
21
|
/**
|
|
31
22
|
* Write .openhands/hooks.json and the pre-tool-check shell script.
|
|
32
23
|
* Also merges llm.base_url into config.toml if it exists.
|
|
@@ -53,25 +44,47 @@ export async function writeOpenHandsHooks(workspaceRoot, proxyUrl, workspaceId =
|
|
|
53
44
|
# Proxy: ${proxyUrl}
|
|
54
45
|
# Generated: ${newIso()}
|
|
55
46
|
set -euo pipefail
|
|
56
|
-
|
|
47
|
+
${SHELL_FAIL_CLOSED}
|
|
57
48
|
INPUT="$(cat)"
|
|
58
|
-
|
|
59
|
-
TARGET_PATH="$(echo "$INPUT" | python3 -c "import sys,json; d=json.load(sys.stdin); i=d.get('tool_input',{}); print(i.get('path',i.get('file_path','')))" 2>/dev/null || true)"
|
|
49
|
+
${SHELL_EXTRACT}
|
|
60
50
|
|
|
61
51
|
HOOK_EVENTS_LOG=${JSON.stringify(hookEventsLog)}
|
|
62
52
|
|
|
63
53
|
# ── Runtime credentials (sourced from ~/.intutic/env/runtime.env) ────────────────────────
|
|
54
|
+
# These are defaults, and they are also the only thing standing between
|
|
55
|
+
# \`set -u\` and a fatal unbound-variable error: log_event reads all three, so on
|
|
56
|
+
# a machine with no runtime.env to source, an unset name kills the gate before
|
|
57
|
+
# it can exit 2. Spelling matters — INTUTIC_, never INTUITIC_.
|
|
64
58
|
RUNTIME_ENV="$HOME/.intutic/env/runtime.env"
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
59
|
+
INTUTIC_HOST="https://api.intutic.ai"
|
|
60
|
+
INTUTIC_API_KEY=""
|
|
61
|
+
INTUTIC_WORKSPACE_ID=${JSON.stringify(workspaceId)}
|
|
68
62
|
[ -f "$RUNTIME_ENV" ] && source "$RUNTIME_ENV" 2>/dev/null || true
|
|
69
63
|
|
|
70
64
|
log_event() {
|
|
71
65
|
local verdict="\$1" tool="\$2" reason="\$3"
|
|
66
|
+
# \`tool\` and \`reason\` carry agent-controlled text — the tool name, and the
|
|
67
|
+
# target path quoted into the block reason — and both are spliced into the
|
|
68
|
+
# JSON string below. Escape first: one unescaped quote yields a line
|
|
69
|
+
# JSON.parse rejects, and drainHookEvents drops malformed lines then wipes the
|
|
70
|
+
# log on a successful drain, so the record of a block is destroyed rather than
|
|
71
|
+
# delayed. A crafted path could otherwise close the string and forge fields.
|
|
72
|
+
tool="\${tool//\\\\/\\\\\\\\}"; tool="\${tool//\\"/\\\\\\"}"
|
|
73
|
+
reason="\${reason//\\\\/\\\\\\\\}"; reason="\${reason//\\"/\\\\\\"}"
|
|
74
|
+
# Every C0 control character, not just newline and carriage return. JSON
|
|
75
|
+
# forbids all of U+0000-U+001F unescaped, so a plain TAB in a filename — no
|
|
76
|
+
# adversary required — was still enough to produce a line JSON.parse rejects.
|
|
77
|
+
# (NUL cannot appear in a bash variable, so the range starts at \\x01.)
|
|
78
|
+
tool="\${tool//[\$'\\x01'-\$'\\x1f']/ }"; reason="\${reason//[\$'\\x01'-\$'\\x1f']/ }"
|
|
79
|
+
# The workspace id is spliced into the same JSON string. It comes from
|
|
80
|
+
# runtime.env on disk rather than from the agent, so it is lower risk — but a
|
|
81
|
+
# value with a quote in it breaks every audit line the same way, and "lower
|
|
82
|
+
# risk" is not "cannot happen" for a file the operator edits by hand.
|
|
83
|
+
local ws="\${INTUTIC_WORKSPACE_ID}"
|
|
84
|
+
ws="\${ws//\\\\/\\\\\\\\}"; ws="\${ws//\\"/\\\\\\"}"; ws="\${ws//[\$'\\x01'-\$'\\x1f']/ }"
|
|
72
85
|
local ts; ts="\$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
73
86
|
local incident_id; incident_id="\$(printf '%s' "\${ts}\${tool}\${INTUTIC_WORKSPACE_ID}" | sha1sum 2>/dev/null | cut -c1-16 || echo \"\$(date +%s)\")"
|
|
74
|
-
local entry="{
|
|
87
|
+
local entry="{\\"event\\":\\"\${verdict}\\",\\"toolName\\":\\"\${tool}\\",\\"reason\\":\\"\${reason}\\",\\"workspaceId\\":\\"\${ws}\\",\\"harnessType\\":\\"openhands\\",\\"timestamp\\":\\"\${ts}\\",\\"incidentId\\":\\"\${incident_id}\\"}"
|
|
75
88
|
# Path B: reliable file append
|
|
76
89
|
printf '%s\\n' "\$entry" >> "\$HOOK_EVENTS_LOG" 2>/dev/null || true
|
|
77
90
|
# Path A: fire-and-forget HTTP (non-blocking, near-real-time)
|
|
@@ -79,32 +92,12 @@ log_event() {
|
|
|
79
92
|
curl -s -o /dev/null --max-time 3 -X POST \\
|
|
80
93
|
-H "Content-Type: application/json" \\
|
|
81
94
|
-H "Authorization: Bearer \$INTUTIC_API_KEY" \\
|
|
82
|
-
-d "{
|
|
95
|
+
-d "{\\"events\\":[\$entry]}" \\
|
|
83
96
|
"\${INTUTIC_HOST}/api/v1/hook-events" & disown 2>/dev/null || true
|
|
84
97
|
fi
|
|
85
98
|
}
|
|
86
99
|
|
|
87
|
-
|
|
88
|
-
".openhands/hooks.json"
|
|
89
|
-
".intutic/hooks"
|
|
90
|
-
".intutic/integrity.json"
|
|
91
|
-
".claude/settings.json"
|
|
92
|
-
".cursor/hooks.json"
|
|
93
|
-
".codeium/windsurf/hooks.json"
|
|
94
|
-
".cline/hooks"
|
|
95
|
-
".agents/plugins/intutic-governance"
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
# Protected-path guard
|
|
99
|
-
for p in "\${PROTECTED_PATHS[@]}"; do
|
|
100
|
-
if echo "$TARGET_PATH" | grep -qF "$p"; then
|
|
101
|
-
REASON="Attempt to modify governance-protected path \\"$TARGET_PATH\\""
|
|
102
|
-
echo "[Intutic Governance] BLOCKED: $REASON" >&2
|
|
103
|
-
log_event "tool_blocked" "\$TOOL_NAME" "\$REASON"
|
|
104
|
-
exit 2
|
|
105
|
-
fi
|
|
106
|
-
done
|
|
107
|
-
|
|
100
|
+
${emitShellGate({ harness: "openhands" })}
|
|
108
101
|
log_event "tool_allowed" "\$TOOL_NAME" ""
|
|
109
102
|
exit 0
|
|
110
103
|
`;
|
|
@@ -152,8 +145,9 @@ async function mergeOpenHandsConfig(workspaceRoot, proxyUrl) {
|
|
|
152
145
|
existing = await fs.readFile(configPath, 'utf-8');
|
|
153
146
|
}
|
|
154
147
|
catch {
|
|
155
|
-
// File doesn't exist —
|
|
156
|
-
existing
|
|
148
|
+
// File doesn't exist (or is unreadable) — deliberate fail-open: fall
|
|
149
|
+
// through with `existing` at its '' initialiser so the block below
|
|
150
|
+
// creates a minimal config instead of aborting the whole sync.
|
|
157
151
|
}
|
|
158
152
|
// Inject or update [llm] section with base_url
|
|
159
153
|
if (existing.includes('[llm]')) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openhandsHooks.js","sourceRoot":"","sources":["../../src/harness/openhandsHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACtC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"openhandsHooks.js","sourceRoot":"","sources":["../../src/harness/openhandsHooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"piHooks.d.ts","sourceRoot":"","sources":["../../src/harness/piHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;
|
|
1
|
+
{"version":3,"file":"piHooks.d.ts","sourceRoot":"","sources":["../../src/harness/piHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAgMH;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAChC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAK,GACf,OAAO,CAAC,IAAI,CAAC,CAuFf"}
|
package/dist/harness/piHooks.js
CHANGED
|
@@ -22,26 +22,12 @@ import * as path from 'node:path';
|
|
|
22
22
|
import * as os from 'node:os';
|
|
23
23
|
import { createLogger } from '@intutic/logger';
|
|
24
24
|
import { newIso } from '@intutic/id';
|
|
25
|
+
import { emitShellGate, SHELL_EXTRACT, SHELL_FAIL_CLOSED } from './gateBody.js';
|
|
25
26
|
const log = createLogger('sync-pi-hooks');
|
|
26
27
|
/** Path to the Pi hooks config. */
|
|
27
28
|
const PI_HOOKS_CONFIG = path.join(os.homedir(), '.pi', 'hooks.json');
|
|
28
29
|
/** Path to the Pi models config. */
|
|
29
30
|
const PI_MODELS_CONFIG = path.join(os.homedir(), '.pi', 'models.json');
|
|
30
|
-
/** Governance-sensitive paths that the hook gate protects. */
|
|
31
|
-
const PROTECTED_PATHS = [
|
|
32
|
-
'.gemini/settings.json',
|
|
33
|
-
'.intutic/hooks',
|
|
34
|
-
'.intutic/integrity.json',
|
|
35
|
-
'.intutic/events',
|
|
36
|
-
'.claude/settings.json',
|
|
37
|
-
'.cursor/hooks.json',
|
|
38
|
-
'.codeium/windsurf/hooks.json',
|
|
39
|
-
'.cline/hooks',
|
|
40
|
-
'.agents/plugins/intutic-governance',
|
|
41
|
-
'.openhands/hooks.json',
|
|
42
|
-
'.pi/hooks.json',
|
|
43
|
-
'.pi/models.json',
|
|
44
|
-
];
|
|
45
31
|
// ─── Bash hook script template ────────────────────────────────────────────────
|
|
46
32
|
function buildPiCheckScript(proxyUrl, workspaceRoot, workspaceId) {
|
|
47
33
|
const hookEventsLog = path.join(workspaceRoot, '.intutic', 'events', 'hook-events.jsonl');
|
|
@@ -53,7 +39,7 @@ function buildPiCheckScript(proxyUrl, workspaceRoot, workspaceId) {
|
|
|
53
39
|
# Generated: ${newIso()}
|
|
54
40
|
# Workspace: ${workspaceId}
|
|
55
41
|
set -euo pipefail
|
|
56
|
-
|
|
42
|
+
${SHELL_FAIL_CLOSED}
|
|
57
43
|
# Source runtime credentials (never embedded in this file)
|
|
58
44
|
if [ -f "${runtimeEnv}" ]; then
|
|
59
45
|
# shellcheck disable=SC1090
|
|
@@ -65,18 +51,39 @@ INTUTIC_API_KEY="\${INTUTIC_API_KEY:-}"
|
|
|
65
51
|
INTUTIC_WORKSPACE_ID="\${INTUTIC_WORKSPACE_ID:-${workspaceId}}"
|
|
66
52
|
|
|
67
53
|
INPUT="$(cat)"
|
|
68
|
-
|
|
69
|
-
TARGET_PATH="$(echo "$INPUT" | python3 -c "import sys,json; d=json.load(sys.stdin); i=d.get('tool_input',{}); print(i.get('path',i.get('file_path','')))" 2>/dev/null || true)"
|
|
70
|
-
COMMAND="$(echo "$INPUT" | python3 -c "import sys,json; d=json.load(sys.stdin); i=d.get('tool_input',{}); print(i.get('command',i.get('cmd','')))" 2>/dev/null || true)"
|
|
54
|
+
${SHELL_EXTRACT}
|
|
71
55
|
|
|
72
56
|
HOOK_EVENTS_LOG="${hookEventsLog}"
|
|
73
57
|
|
|
74
58
|
log_event() {
|
|
75
59
|
local verdict="$1" tool="$2" reason="$3"
|
|
60
|
+
# \`tool\`, \`reason\` and \`SESSION_ID\` carry agent-controlled text — the tool
|
|
61
|
+
# name, the target path quoted into the block reason, and a field lifted
|
|
62
|
+
# straight out of the hook's stdin — and all three are spliced into the JSON
|
|
63
|
+
# string below. Escape first: one unescaped quote yields a line JSON.parse
|
|
64
|
+
# rejects, and drainHookEvents drops malformed lines then wipes the log on a
|
|
65
|
+
# successful drain, so the record of a block is destroyed rather than delayed.
|
|
66
|
+
# A crafted path could otherwise close the string and forge fields.
|
|
67
|
+
tool="\${tool//\\\\/\\\\\\\\}"; tool="\${tool//\\"/\\\\\\"}"
|
|
68
|
+
reason="\${reason//\\\\/\\\\\\\\}"; reason="\${reason//\\"/\\\\\\"}"
|
|
69
|
+
# Every C0 control character, not just newline and carriage return. JSON
|
|
70
|
+
# forbids all of U+0000-U+001F unescaped, so a plain TAB in a filename — no
|
|
71
|
+
# adversary required — was still enough to produce a line JSON.parse rejects.
|
|
72
|
+
# (NUL cannot appear in a bash variable, so the range starts at \\x01.)
|
|
73
|
+
tool="\${tool//[\$'\\x01'-\$'\\x1f']/ }"; reason="\${reason//[\$'\\x01'-\$'\\x1f']/ }"
|
|
74
|
+
# The workspace id is spliced into the same JSON string. It comes from
|
|
75
|
+
# runtime.env on disk rather than from the agent, so it is lower risk — but a
|
|
76
|
+
# value with a quote in it breaks every audit line the same way, and "lower
|
|
77
|
+
# risk" is not "cannot happen" for a file the operator edits by hand.
|
|
78
|
+
local ws="\${INTUTIC_WORKSPACE_ID}"
|
|
79
|
+
ws="\${ws//\\\\/\\\\\\\\}"; ws="\${ws//\\"/\\\\\\"}"; ws="\${ws//[\$'\\x01'-\$'\\x1f']/ }"
|
|
80
|
+
local sid="\$SESSION_ID"
|
|
81
|
+
sid="\${sid//\\\\/\\\\\\\\}"; sid="\${sid//\\"/\\\\\\"}"
|
|
82
|
+
sid="\${sid//\$'\\n'/ }"; sid="\${sid//\$'\\r'/ }"
|
|
76
83
|
local ts; ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
77
84
|
# incidentId = sha1(timestamp + toolName + workspaceId).slice(0,16)
|
|
78
85
|
local incident_id; incident_id="$(printf '%s' "\${ts}\${tool}\${INTUTIC_WORKSPACE_ID}" | sha1sum 2>/dev/null | cut -c1-16 || echo "$(date +%s)")"
|
|
79
|
-
local entry="{\\"event\\":\\"
|
|
86
|
+
local entry="{\\"event\\":\\"\${verdict}\\",\\"toolName\\":\\"\${tool}\\",\\"reason\\":\\"\${reason}\\",\\"workspaceId\\":\\"\${ws}\\",\\"harnessType\\":\\"pi\\",\\"incidentId\\":\\"\${incident_id}\\",\\"timestamp\\":\\"\${ts}\\"\${sid:+,\\"sessionId\\":\\"\${sid}\\"}}"
|
|
80
87
|
# Path B: reliable file append (sync-daemon drains on FSEvents change)
|
|
81
88
|
printf '%s\\n' "\$entry" >> "\$HOOK_EVENTS_LOG" 2>/dev/null || true
|
|
82
89
|
# Path A: fire-and-forget HTTP POST (near-real-time dashboard, non-blocking)
|
|
@@ -89,41 +96,7 @@ log_event() {
|
|
|
89
96
|
fi
|
|
90
97
|
}
|
|
91
98
|
|
|
92
|
-
|
|
93
|
-
${PROTECTED_PATHS.map((p) => ` "${p}"`).join('\n')}
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
# 1. Protected-path guard
|
|
97
|
-
for p in "\${PROTECTED_PATHS[@]}"; do
|
|
98
|
-
if echo "$TARGET_PATH" | grep -qF "$p"; then
|
|
99
|
-
REASON="Attempt to modify governance-protected path \\"$TARGET_PATH\\""
|
|
100
|
-
echo "[Intutic Governance] BLOCKED: $REASON" >&2
|
|
101
|
-
log_event "tool_blocked" "$TOOL_NAME" "$REASON"
|
|
102
|
-
exit 2
|
|
103
|
-
fi
|
|
104
|
-
done
|
|
105
|
-
|
|
106
|
-
# 2. Bash/shell command guard — block governance bypass patterns
|
|
107
|
-
if [ -n "$COMMAND" ]; then
|
|
108
|
-
BYPASS_PATTERNS=(
|
|
109
|
-
"chflags nouchg"
|
|
110
|
-
"chattr -i"
|
|
111
|
-
"rm -rf .intutic"
|
|
112
|
-
"rm -rf .pi/hooks"
|
|
113
|
-
"rm -rf .pi/models"
|
|
114
|
-
"PI_PRE_TOOL_HOOK="
|
|
115
|
-
"rm -rf .gemini/settings"
|
|
116
|
-
)
|
|
117
|
-
for pattern in "\${BYPASS_PATTERNS[@]}"; do
|
|
118
|
-
if echo "$COMMAND" | grep -qF "$pattern"; then
|
|
119
|
-
REASON="Shell command matches governance bypass pattern: \\"$pattern\\""
|
|
120
|
-
echo "[Intutic Governance] BLOCKED: $REASON" >&2
|
|
121
|
-
log_event "tool_blocked" "$TOOL_NAME" "$REASON"
|
|
122
|
-
exit 2
|
|
123
|
-
fi
|
|
124
|
-
done
|
|
125
|
-
fi
|
|
126
|
-
|
|
99
|
+
${emitShellGate({ harness: "pi" })}
|
|
127
100
|
log_event "tool_allowed" "$TOOL_NAME" ""
|
|
128
101
|
exit 0
|
|
129
102
|
`;
|
|
@@ -242,9 +215,9 @@ export async function writePiHooks(workspaceRoot, proxyUrl, workspaceId = '') {
|
|
|
242
215
|
// ── 5. Write env snippet ───────────────────────────────────────────────────
|
|
243
216
|
const envSnippet = [
|
|
244
217
|
`# Intutic pi governance env`,
|
|
245
|
-
`
|
|
246
|
-
`
|
|
247
|
-
`
|
|
218
|
+
`INTUTIC_PI_HOOK=~/.intutic/hooks/pi-check.sh`,
|
|
219
|
+
`INTUTIC_PI_HOOKS_CONFIG=~/.pi/hooks.json`,
|
|
220
|
+
`INTUTIC_PI_MODELS_CONFIG=~/.pi/models.json`,
|
|
248
221
|
`OPENAI_BASE_URL=${proxyUrl}`,
|
|
249
222
|
`ANTHROPIC_BASE_URL=${proxyUrl}`,
|
|
250
223
|
].join('\n') + '\n';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"piHooks.js","sourceRoot":"","sources":["../../src/harness/piHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACtC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAA;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"piHooks.js","sourceRoot":"","sources":["../../src/harness/piHooks.ts"],"names":[],"mappings":"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"}
|