fastgrc-openclaw 1.0.30 → 1.0.33
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/dist/bin.js +95 -29
- package/dist/bin.mjs +95 -29
- package/dist/plugin.js +15 -10
- package/dist/plugin.mjs +15 -10
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -112,7 +112,7 @@ function startExecApprovalsServer(apiKey, policyId, baseUrl) {
|
|
|
112
112
|
console.warn("[fastgrc] exec-approvals: no socket config found \u2014 webchat execs will not be evaluated");
|
|
113
113
|
return;
|
|
114
114
|
}
|
|
115
|
-
const { path: sockPath
|
|
115
|
+
const { path: sockPath } = cfg.socket;
|
|
116
116
|
try {
|
|
117
117
|
fs2.unlinkSync(sockPath);
|
|
118
118
|
} catch {
|
|
@@ -126,18 +126,25 @@ function startExecApprovalsServer(apiKey, policyId, baseUrl) {
|
|
|
126
126
|
const line = buffer.slice(0, idx).trim();
|
|
127
127
|
buffer = "";
|
|
128
128
|
if (!line) {
|
|
129
|
-
conn.
|
|
129
|
+
conn.end();
|
|
130
130
|
return;
|
|
131
131
|
}
|
|
132
132
|
let msg;
|
|
133
133
|
try {
|
|
134
134
|
msg = JSON.parse(line);
|
|
135
135
|
} catch {
|
|
136
|
-
conn.
|
|
136
|
+
conn.end();
|
|
137
137
|
return;
|
|
138
138
|
}
|
|
139
|
-
if (msg.type !== "request"
|
|
140
|
-
conn.
|
|
139
|
+
if (msg.type !== "request") {
|
|
140
|
+
conn.end();
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const liveCfg = readExecApprovalsConfig();
|
|
144
|
+
const expectedToken = liveCfg?.socket?.token;
|
|
145
|
+
if (!expectedToken || msg.token !== expectedToken) {
|
|
146
|
+
console.warn("[fastgrc] exec-approvals: token mismatch \u2014 failing open. Restart fastgrc-approvals service if this persists.");
|
|
147
|
+
conn.end(JSON.stringify({ type: "decision", decision: "allow-once" }) + "\n");
|
|
141
148
|
return;
|
|
142
149
|
}
|
|
143
150
|
const req = msg.request ?? {};
|
|
@@ -157,11 +164,9 @@ function startExecApprovalsServer(apiKey, policyId, baseUrl) {
|
|
|
157
164
|
const rule = result.policyContext?.matchedRule;
|
|
158
165
|
console.warn(rule ? `[fastgrc] exec blocked: [${rule}] ${result.reasoning}` : `[fastgrc] exec blocked: ${result.reasoning}`);
|
|
159
166
|
}
|
|
160
|
-
conn.
|
|
161
|
-
conn.destroy();
|
|
167
|
+
conn.end(JSON.stringify({ type: "decision", decision }) + "\n");
|
|
162
168
|
}).catch(() => {
|
|
163
|
-
conn.
|
|
164
|
-
conn.destroy();
|
|
169
|
+
conn.end(JSON.stringify({ type: "decision", decision: "allow-once" }) + "\n");
|
|
165
170
|
});
|
|
166
171
|
});
|
|
167
172
|
conn.on("error", () => {
|
|
@@ -169,7 +174,7 @@ function startExecApprovalsServer(apiKey, policyId, baseUrl) {
|
|
|
169
174
|
});
|
|
170
175
|
server.listen(sockPath, () => {
|
|
171
176
|
try {
|
|
172
|
-
fs2.chmodSync(sockPath,
|
|
177
|
+
fs2.chmodSync(sockPath, 432);
|
|
173
178
|
} catch {
|
|
174
179
|
}
|
|
175
180
|
console.log(`[fastgrc] exec-approvals server listening on ${sockPath}`);
|
|
@@ -286,6 +291,24 @@ function doUninstallHook(targetDir) {
|
|
|
286
291
|
`);
|
|
287
292
|
}
|
|
288
293
|
}
|
|
294
|
+
async function validatePolicy(apiKey, policyId, baseUrl = "https://app.fastgrc.ai") {
|
|
295
|
+
try {
|
|
296
|
+
const controller = new AbortController();
|
|
297
|
+
setTimeout(() => controller.abort(), 5e3);
|
|
298
|
+
const res = await fetch(`${baseUrl.replace(/\/$/, "")}/api/v1/policy-router/policies/${encodeURIComponent(policyId)}`, {
|
|
299
|
+
headers: { "Authorization": `Bearer ${apiKey}` },
|
|
300
|
+
signal: controller.signal
|
|
301
|
+
});
|
|
302
|
+
if (res.status === 404) return { valid: false, error: "Policy not found \u2014 check the ID belongs to your account." };
|
|
303
|
+
if (res.status === 401) return { valid: false, error: "Invalid API key." };
|
|
304
|
+
if (!res.ok) return { valid: false, error: `API returned ${res.status}` };
|
|
305
|
+
const policy = await res.json();
|
|
306
|
+
return { valid: true, name: policy.name };
|
|
307
|
+
} catch (err) {
|
|
308
|
+
const reason = err instanceof Error && err.name === "AbortError" ? "timeout" : String(err);
|
|
309
|
+
return { valid: false, error: `Could not reach FastGRC API (${reason}) \u2014 skipping validation.` };
|
|
310
|
+
}
|
|
311
|
+
}
|
|
289
312
|
function doConfigureExecApprovals() {
|
|
290
313
|
const cfgPath = path3.join(os3.homedir(), ".openclaw", "exec-approvals.json");
|
|
291
314
|
if (!fs3.existsSync(cfgPath)) {
|
|
@@ -301,9 +324,9 @@ function doConfigureExecApprovals() {
|
|
|
301
324
|
const updated = {
|
|
302
325
|
...existing,
|
|
303
326
|
defaults: {
|
|
304
|
-
security: "
|
|
327
|
+
security: "ask",
|
|
305
328
|
ask: "always",
|
|
306
|
-
askFallback: "
|
|
329
|
+
askFallback: "allow",
|
|
307
330
|
autoAllowSkills: false
|
|
308
331
|
}
|
|
309
332
|
};
|
|
@@ -345,11 +368,29 @@ if (cmd === "set-policy") {
|
|
|
345
368
|
process.stderr.write("Usage: fastgrc-hook set-policy <policy-id>\n");
|
|
346
369
|
process.exit(1);
|
|
347
370
|
}
|
|
348
|
-
|
|
349
|
-
|
|
371
|
+
(async () => {
|
|
372
|
+
const apiKey = readConfig().apiKey ?? process.env.FASTGRC_API_KEY;
|
|
373
|
+
const baseUrl = process.env.FASTGRC_BASE_URL ?? "https://app.fastgrc.ai";
|
|
374
|
+
if (apiKey) {
|
|
375
|
+
process.stdout.write(`Verifying policy ${arg}\u2026
|
|
350
376
|
`);
|
|
351
|
-
|
|
352
|
-
|
|
377
|
+
const check = await validatePolicy(apiKey, arg, baseUrl);
|
|
378
|
+
if (!check.valid) {
|
|
379
|
+
process.stderr.write(`\u2717 ${check.error}
|
|
380
|
+
`);
|
|
381
|
+
process.exit(1);
|
|
382
|
+
}
|
|
383
|
+
process.stdout.write(`\u2713 Policy verified: ${check.name}
|
|
384
|
+
`);
|
|
385
|
+
} else {
|
|
386
|
+
process.stdout.write("\u26A0 No API key set \u2014 skipping policy verification.\n");
|
|
387
|
+
}
|
|
388
|
+
writeConfig({ ...readConfig(), policyId: arg });
|
|
389
|
+
process.stdout.write(`\u2713 FastGRC policy ID saved to ${CONFIG_PATH}
|
|
390
|
+
`);
|
|
391
|
+
process.stdout.write(' Run "fastgrc-hook get-policy" to verify.\n');
|
|
392
|
+
process.exit(0);
|
|
393
|
+
})();
|
|
353
394
|
}
|
|
354
395
|
if (cmd === "get-policy") {
|
|
355
396
|
const id = readConfig().policyId;
|
|
@@ -396,19 +437,33 @@ if (cmd === "setup") {
|
|
|
396
437
|
process.stderr.write("Usage: fastgrc-hook setup --api-key <key> [--policy-id <id>]\n");
|
|
397
438
|
process.exit(1);
|
|
398
439
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
440
|
+
(async () => {
|
|
441
|
+
const baseUrl = process.env.FASTGRC_BASE_URL ?? "https://app.fastgrc.ai";
|
|
442
|
+
writeConfig({ ...readConfig(), apiKey: apiKeyArg });
|
|
443
|
+
process.stdout.write("\u2713 API key saved to ~/.fastgrc.json\n");
|
|
444
|
+
if (policyIdArg) {
|
|
445
|
+
process.stdout.write(`Verifying policy ${policyIdArg}\u2026
|
|
446
|
+
`);
|
|
447
|
+
const check = await validatePolicy(apiKeyArg, policyIdArg, baseUrl);
|
|
448
|
+
if (!check.valid) {
|
|
449
|
+
process.stderr.write(`\u2717 Policy not found: ${check.error}
|
|
450
|
+
`);
|
|
451
|
+
process.stderr.write(" Pass a valid policy ID from your dashboard, or omit --policy-id to use the org default.\n");
|
|
452
|
+
process.exit(1);
|
|
453
|
+
}
|
|
454
|
+
process.stdout.write(`\u2713 Policy verified: ${check.name}
|
|
455
|
+
`);
|
|
456
|
+
writeConfig({ ...readConfig(), policyId: policyIdArg });
|
|
457
|
+
process.stdout.write("\u2713 Policy ID saved to ~/.fastgrc.json\n");
|
|
458
|
+
} else {
|
|
459
|
+
process.stdout.write(" (no policy ID \u2014 org-wide default will be used)\n");
|
|
460
|
+
}
|
|
461
|
+
doInstallHook(process.cwd());
|
|
462
|
+
doConfigureExecApprovals();
|
|
463
|
+
process.stdout.write("\n\u2713 Config, HOOK.md, and exec-approvals done.\n");
|
|
464
|
+
process.stdout.write('Restart OpenClaw, then run "fastgrc-hook test" to verify.\n');
|
|
465
|
+
process.exit(0);
|
|
466
|
+
})();
|
|
412
467
|
}
|
|
413
468
|
if (cmd === "serve-approvals") {
|
|
414
469
|
const apiKey = resolveApiKey();
|
|
@@ -423,6 +478,17 @@ if (cmd === "serve-approvals") {
|
|
|
423
478
|
);
|
|
424
479
|
process.exit(1);
|
|
425
480
|
}
|
|
481
|
+
try {
|
|
482
|
+
const cfgPath = path3.join(os3.homedir(), ".openclaw", "exec-approvals.json");
|
|
483
|
+
const raw = JSON.parse(fs3.readFileSync(cfgPath, "utf8"));
|
|
484
|
+
const defaults = raw.defaults ?? {};
|
|
485
|
+
if (defaults.security !== "ask" || defaults.askFallback !== "allow") {
|
|
486
|
+
raw.defaults = { ...defaults, security: "ask", askFallback: "allow" };
|
|
487
|
+
fs3.writeFileSync(cfgPath, JSON.stringify(raw, null, 2), "utf8");
|
|
488
|
+
process.stdout.write("[fastgrc] exec-approvals defaults patched: security=ask, askFallback=allow\n");
|
|
489
|
+
}
|
|
490
|
+
} catch {
|
|
491
|
+
}
|
|
426
492
|
const policyId = process.env.FASTGRC_POLICY_ID ?? readConfig().policyId;
|
|
427
493
|
const baseUrl = process.env.FASTGRC_BASE_URL ?? "https://app.fastgrc.ai";
|
|
428
494
|
process.stdout.write(`[fastgrc] serve-approvals running \u2014 listening on ${cfg.socket.path}
|
package/dist/bin.mjs
CHANGED
|
@@ -89,7 +89,7 @@ function startExecApprovalsServer(apiKey, policyId, baseUrl) {
|
|
|
89
89
|
console.warn("[fastgrc] exec-approvals: no socket config found \u2014 webchat execs will not be evaluated");
|
|
90
90
|
return;
|
|
91
91
|
}
|
|
92
|
-
const { path: sockPath
|
|
92
|
+
const { path: sockPath } = cfg.socket;
|
|
93
93
|
try {
|
|
94
94
|
fs2.unlinkSync(sockPath);
|
|
95
95
|
} catch {
|
|
@@ -103,18 +103,25 @@ function startExecApprovalsServer(apiKey, policyId, baseUrl) {
|
|
|
103
103
|
const line = buffer.slice(0, idx).trim();
|
|
104
104
|
buffer = "";
|
|
105
105
|
if (!line) {
|
|
106
|
-
conn.
|
|
106
|
+
conn.end();
|
|
107
107
|
return;
|
|
108
108
|
}
|
|
109
109
|
let msg;
|
|
110
110
|
try {
|
|
111
111
|
msg = JSON.parse(line);
|
|
112
112
|
} catch {
|
|
113
|
-
conn.
|
|
113
|
+
conn.end();
|
|
114
114
|
return;
|
|
115
115
|
}
|
|
116
|
-
if (msg.type !== "request"
|
|
117
|
-
conn.
|
|
116
|
+
if (msg.type !== "request") {
|
|
117
|
+
conn.end();
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const liveCfg = readExecApprovalsConfig();
|
|
121
|
+
const expectedToken = liveCfg?.socket?.token;
|
|
122
|
+
if (!expectedToken || msg.token !== expectedToken) {
|
|
123
|
+
console.warn("[fastgrc] exec-approvals: token mismatch \u2014 failing open. Restart fastgrc-approvals service if this persists.");
|
|
124
|
+
conn.end(JSON.stringify({ type: "decision", decision: "allow-once" }) + "\n");
|
|
118
125
|
return;
|
|
119
126
|
}
|
|
120
127
|
const req = msg.request ?? {};
|
|
@@ -134,11 +141,9 @@ function startExecApprovalsServer(apiKey, policyId, baseUrl) {
|
|
|
134
141
|
const rule = result.policyContext?.matchedRule;
|
|
135
142
|
console.warn(rule ? `[fastgrc] exec blocked: [${rule}] ${result.reasoning}` : `[fastgrc] exec blocked: ${result.reasoning}`);
|
|
136
143
|
}
|
|
137
|
-
conn.
|
|
138
|
-
conn.destroy();
|
|
144
|
+
conn.end(JSON.stringify({ type: "decision", decision }) + "\n");
|
|
139
145
|
}).catch(() => {
|
|
140
|
-
conn.
|
|
141
|
-
conn.destroy();
|
|
146
|
+
conn.end(JSON.stringify({ type: "decision", decision: "allow-once" }) + "\n");
|
|
142
147
|
});
|
|
143
148
|
});
|
|
144
149
|
conn.on("error", () => {
|
|
@@ -146,7 +151,7 @@ function startExecApprovalsServer(apiKey, policyId, baseUrl) {
|
|
|
146
151
|
});
|
|
147
152
|
server.listen(sockPath, () => {
|
|
148
153
|
try {
|
|
149
|
-
fs2.chmodSync(sockPath,
|
|
154
|
+
fs2.chmodSync(sockPath, 432);
|
|
150
155
|
} catch {
|
|
151
156
|
}
|
|
152
157
|
console.log(`[fastgrc] exec-approvals server listening on ${sockPath}`);
|
|
@@ -263,6 +268,24 @@ function doUninstallHook(targetDir) {
|
|
|
263
268
|
`);
|
|
264
269
|
}
|
|
265
270
|
}
|
|
271
|
+
async function validatePolicy(apiKey, policyId, baseUrl = "https://app.fastgrc.ai") {
|
|
272
|
+
try {
|
|
273
|
+
const controller = new AbortController();
|
|
274
|
+
setTimeout(() => controller.abort(), 5e3);
|
|
275
|
+
const res = await fetch(`${baseUrl.replace(/\/$/, "")}/api/v1/policy-router/policies/${encodeURIComponent(policyId)}`, {
|
|
276
|
+
headers: { "Authorization": `Bearer ${apiKey}` },
|
|
277
|
+
signal: controller.signal
|
|
278
|
+
});
|
|
279
|
+
if (res.status === 404) return { valid: false, error: "Policy not found \u2014 check the ID belongs to your account." };
|
|
280
|
+
if (res.status === 401) return { valid: false, error: "Invalid API key." };
|
|
281
|
+
if (!res.ok) return { valid: false, error: `API returned ${res.status}` };
|
|
282
|
+
const policy = await res.json();
|
|
283
|
+
return { valid: true, name: policy.name };
|
|
284
|
+
} catch (err) {
|
|
285
|
+
const reason = err instanceof Error && err.name === "AbortError" ? "timeout" : String(err);
|
|
286
|
+
return { valid: false, error: `Could not reach FastGRC API (${reason}) \u2014 skipping validation.` };
|
|
287
|
+
}
|
|
288
|
+
}
|
|
266
289
|
function doConfigureExecApprovals() {
|
|
267
290
|
const cfgPath = path3.join(os3.homedir(), ".openclaw", "exec-approvals.json");
|
|
268
291
|
if (!fs3.existsSync(cfgPath)) {
|
|
@@ -278,9 +301,9 @@ function doConfigureExecApprovals() {
|
|
|
278
301
|
const updated = {
|
|
279
302
|
...existing,
|
|
280
303
|
defaults: {
|
|
281
|
-
security: "
|
|
304
|
+
security: "ask",
|
|
282
305
|
ask: "always",
|
|
283
|
-
askFallback: "
|
|
306
|
+
askFallback: "allow",
|
|
284
307
|
autoAllowSkills: false
|
|
285
308
|
}
|
|
286
309
|
};
|
|
@@ -322,11 +345,29 @@ if (cmd === "set-policy") {
|
|
|
322
345
|
process.stderr.write("Usage: fastgrc-hook set-policy <policy-id>\n");
|
|
323
346
|
process.exit(1);
|
|
324
347
|
}
|
|
325
|
-
|
|
326
|
-
|
|
348
|
+
(async () => {
|
|
349
|
+
const apiKey = readConfig().apiKey ?? process.env.FASTGRC_API_KEY;
|
|
350
|
+
const baseUrl = process.env.FASTGRC_BASE_URL ?? "https://app.fastgrc.ai";
|
|
351
|
+
if (apiKey) {
|
|
352
|
+
process.stdout.write(`Verifying policy ${arg}\u2026
|
|
327
353
|
`);
|
|
328
|
-
|
|
329
|
-
|
|
354
|
+
const check = await validatePolicy(apiKey, arg, baseUrl);
|
|
355
|
+
if (!check.valid) {
|
|
356
|
+
process.stderr.write(`\u2717 ${check.error}
|
|
357
|
+
`);
|
|
358
|
+
process.exit(1);
|
|
359
|
+
}
|
|
360
|
+
process.stdout.write(`\u2713 Policy verified: ${check.name}
|
|
361
|
+
`);
|
|
362
|
+
} else {
|
|
363
|
+
process.stdout.write("\u26A0 No API key set \u2014 skipping policy verification.\n");
|
|
364
|
+
}
|
|
365
|
+
writeConfig({ ...readConfig(), policyId: arg });
|
|
366
|
+
process.stdout.write(`\u2713 FastGRC policy ID saved to ${CONFIG_PATH}
|
|
367
|
+
`);
|
|
368
|
+
process.stdout.write(' Run "fastgrc-hook get-policy" to verify.\n');
|
|
369
|
+
process.exit(0);
|
|
370
|
+
})();
|
|
330
371
|
}
|
|
331
372
|
if (cmd === "get-policy") {
|
|
332
373
|
const id = readConfig().policyId;
|
|
@@ -373,19 +414,33 @@ if (cmd === "setup") {
|
|
|
373
414
|
process.stderr.write("Usage: fastgrc-hook setup --api-key <key> [--policy-id <id>]\n");
|
|
374
415
|
process.exit(1);
|
|
375
416
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
417
|
+
(async () => {
|
|
418
|
+
const baseUrl = process.env.FASTGRC_BASE_URL ?? "https://app.fastgrc.ai";
|
|
419
|
+
writeConfig({ ...readConfig(), apiKey: apiKeyArg });
|
|
420
|
+
process.stdout.write("\u2713 API key saved to ~/.fastgrc.json\n");
|
|
421
|
+
if (policyIdArg) {
|
|
422
|
+
process.stdout.write(`Verifying policy ${policyIdArg}\u2026
|
|
423
|
+
`);
|
|
424
|
+
const check = await validatePolicy(apiKeyArg, policyIdArg, baseUrl);
|
|
425
|
+
if (!check.valid) {
|
|
426
|
+
process.stderr.write(`\u2717 Policy not found: ${check.error}
|
|
427
|
+
`);
|
|
428
|
+
process.stderr.write(" Pass a valid policy ID from your dashboard, or omit --policy-id to use the org default.\n");
|
|
429
|
+
process.exit(1);
|
|
430
|
+
}
|
|
431
|
+
process.stdout.write(`\u2713 Policy verified: ${check.name}
|
|
432
|
+
`);
|
|
433
|
+
writeConfig({ ...readConfig(), policyId: policyIdArg });
|
|
434
|
+
process.stdout.write("\u2713 Policy ID saved to ~/.fastgrc.json\n");
|
|
435
|
+
} else {
|
|
436
|
+
process.stdout.write(" (no policy ID \u2014 org-wide default will be used)\n");
|
|
437
|
+
}
|
|
438
|
+
doInstallHook(process.cwd());
|
|
439
|
+
doConfigureExecApprovals();
|
|
440
|
+
process.stdout.write("\n\u2713 Config, HOOK.md, and exec-approvals done.\n");
|
|
441
|
+
process.stdout.write('Restart OpenClaw, then run "fastgrc-hook test" to verify.\n');
|
|
442
|
+
process.exit(0);
|
|
443
|
+
})();
|
|
389
444
|
}
|
|
390
445
|
if (cmd === "serve-approvals") {
|
|
391
446
|
const apiKey = resolveApiKey();
|
|
@@ -400,6 +455,17 @@ if (cmd === "serve-approvals") {
|
|
|
400
455
|
);
|
|
401
456
|
process.exit(1);
|
|
402
457
|
}
|
|
458
|
+
try {
|
|
459
|
+
const cfgPath = path3.join(os3.homedir(), ".openclaw", "exec-approvals.json");
|
|
460
|
+
const raw = JSON.parse(fs3.readFileSync(cfgPath, "utf8"));
|
|
461
|
+
const defaults = raw.defaults ?? {};
|
|
462
|
+
if (defaults.security !== "ask" || defaults.askFallback !== "allow") {
|
|
463
|
+
raw.defaults = { ...defaults, security: "ask", askFallback: "allow" };
|
|
464
|
+
fs3.writeFileSync(cfgPath, JSON.stringify(raw, null, 2), "utf8");
|
|
465
|
+
process.stdout.write("[fastgrc] exec-approvals defaults patched: security=ask, askFallback=allow\n");
|
|
466
|
+
}
|
|
467
|
+
} catch {
|
|
468
|
+
}
|
|
403
469
|
const policyId = process.env.FASTGRC_POLICY_ID ?? readConfig().policyId;
|
|
404
470
|
const baseUrl = process.env.FASTGRC_BASE_URL ?? "https://app.fastgrc.ai";
|
|
405
471
|
process.stdout.write(`[fastgrc] serve-approvals running \u2014 listening on ${cfg.socket.path}
|
package/dist/plugin.js
CHANGED
|
@@ -134,7 +134,7 @@ function startExecApprovalsServer(apiKey, policyId, baseUrl) {
|
|
|
134
134
|
console.warn("[fastgrc] exec-approvals: no socket config found \u2014 webchat execs will not be evaluated");
|
|
135
135
|
return;
|
|
136
136
|
}
|
|
137
|
-
const { path: sockPath
|
|
137
|
+
const { path: sockPath } = cfg.socket;
|
|
138
138
|
try {
|
|
139
139
|
fs2.unlinkSync(sockPath);
|
|
140
140
|
} catch {
|
|
@@ -148,18 +148,25 @@ function startExecApprovalsServer(apiKey, policyId, baseUrl) {
|
|
|
148
148
|
const line = buffer.slice(0, idx).trim();
|
|
149
149
|
buffer = "";
|
|
150
150
|
if (!line) {
|
|
151
|
-
conn.
|
|
151
|
+
conn.end();
|
|
152
152
|
return;
|
|
153
153
|
}
|
|
154
154
|
let msg;
|
|
155
155
|
try {
|
|
156
156
|
msg = JSON.parse(line);
|
|
157
157
|
} catch {
|
|
158
|
-
conn.
|
|
158
|
+
conn.end();
|
|
159
159
|
return;
|
|
160
160
|
}
|
|
161
|
-
if (msg.type !== "request"
|
|
162
|
-
conn.
|
|
161
|
+
if (msg.type !== "request") {
|
|
162
|
+
conn.end();
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const liveCfg = readExecApprovalsConfig();
|
|
166
|
+
const expectedToken = liveCfg?.socket?.token;
|
|
167
|
+
if (!expectedToken || msg.token !== expectedToken) {
|
|
168
|
+
console.warn("[fastgrc] exec-approvals: token mismatch \u2014 failing open. Restart fastgrc-approvals service if this persists.");
|
|
169
|
+
conn.end(JSON.stringify({ type: "decision", decision: "allow-once" }) + "\n");
|
|
163
170
|
return;
|
|
164
171
|
}
|
|
165
172
|
const req = msg.request ?? {};
|
|
@@ -179,11 +186,9 @@ function startExecApprovalsServer(apiKey, policyId, baseUrl) {
|
|
|
179
186
|
const rule = result.policyContext?.matchedRule;
|
|
180
187
|
console.warn(rule ? `[fastgrc] exec blocked: [${rule}] ${result.reasoning}` : `[fastgrc] exec blocked: ${result.reasoning}`);
|
|
181
188
|
}
|
|
182
|
-
conn.
|
|
183
|
-
conn.destroy();
|
|
189
|
+
conn.end(JSON.stringify({ type: "decision", decision }) + "\n");
|
|
184
190
|
}).catch(() => {
|
|
185
|
-
conn.
|
|
186
|
-
conn.destroy();
|
|
191
|
+
conn.end(JSON.stringify({ type: "decision", decision: "allow-once" }) + "\n");
|
|
187
192
|
});
|
|
188
193
|
});
|
|
189
194
|
conn.on("error", () => {
|
|
@@ -191,7 +196,7 @@ function startExecApprovalsServer(apiKey, policyId, baseUrl) {
|
|
|
191
196
|
});
|
|
192
197
|
server.listen(sockPath, () => {
|
|
193
198
|
try {
|
|
194
|
-
fs2.chmodSync(sockPath,
|
|
199
|
+
fs2.chmodSync(sockPath, 432);
|
|
195
200
|
} catch {
|
|
196
201
|
}
|
|
197
202
|
console.log(`[fastgrc] exec-approvals server listening on ${sockPath}`);
|
package/dist/plugin.mjs
CHANGED
|
@@ -98,7 +98,7 @@ function startExecApprovalsServer(apiKey, policyId, baseUrl) {
|
|
|
98
98
|
console.warn("[fastgrc] exec-approvals: no socket config found \u2014 webchat execs will not be evaluated");
|
|
99
99
|
return;
|
|
100
100
|
}
|
|
101
|
-
const { path: sockPath
|
|
101
|
+
const { path: sockPath } = cfg.socket;
|
|
102
102
|
try {
|
|
103
103
|
fs2.unlinkSync(sockPath);
|
|
104
104
|
} catch {
|
|
@@ -112,18 +112,25 @@ function startExecApprovalsServer(apiKey, policyId, baseUrl) {
|
|
|
112
112
|
const line = buffer.slice(0, idx).trim();
|
|
113
113
|
buffer = "";
|
|
114
114
|
if (!line) {
|
|
115
|
-
conn.
|
|
115
|
+
conn.end();
|
|
116
116
|
return;
|
|
117
117
|
}
|
|
118
118
|
let msg;
|
|
119
119
|
try {
|
|
120
120
|
msg = JSON.parse(line);
|
|
121
121
|
} catch {
|
|
122
|
-
conn.
|
|
122
|
+
conn.end();
|
|
123
123
|
return;
|
|
124
124
|
}
|
|
125
|
-
if (msg.type !== "request"
|
|
126
|
-
conn.
|
|
125
|
+
if (msg.type !== "request") {
|
|
126
|
+
conn.end();
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const liveCfg = readExecApprovalsConfig();
|
|
130
|
+
const expectedToken = liveCfg?.socket?.token;
|
|
131
|
+
if (!expectedToken || msg.token !== expectedToken) {
|
|
132
|
+
console.warn("[fastgrc] exec-approvals: token mismatch \u2014 failing open. Restart fastgrc-approvals service if this persists.");
|
|
133
|
+
conn.end(JSON.stringify({ type: "decision", decision: "allow-once" }) + "\n");
|
|
127
134
|
return;
|
|
128
135
|
}
|
|
129
136
|
const req = msg.request ?? {};
|
|
@@ -143,11 +150,9 @@ function startExecApprovalsServer(apiKey, policyId, baseUrl) {
|
|
|
143
150
|
const rule = result.policyContext?.matchedRule;
|
|
144
151
|
console.warn(rule ? `[fastgrc] exec blocked: [${rule}] ${result.reasoning}` : `[fastgrc] exec blocked: ${result.reasoning}`);
|
|
145
152
|
}
|
|
146
|
-
conn.
|
|
147
|
-
conn.destroy();
|
|
153
|
+
conn.end(JSON.stringify({ type: "decision", decision }) + "\n");
|
|
148
154
|
}).catch(() => {
|
|
149
|
-
conn.
|
|
150
|
-
conn.destroy();
|
|
155
|
+
conn.end(JSON.stringify({ type: "decision", decision: "allow-once" }) + "\n");
|
|
151
156
|
});
|
|
152
157
|
});
|
|
153
158
|
conn.on("error", () => {
|
|
@@ -155,7 +160,7 @@ function startExecApprovalsServer(apiKey, policyId, baseUrl) {
|
|
|
155
160
|
});
|
|
156
161
|
server.listen(sockPath, () => {
|
|
157
162
|
try {
|
|
158
|
-
fs2.chmodSync(sockPath,
|
|
163
|
+
fs2.chmodSync(sockPath, 432);
|
|
159
164
|
} catch {
|
|
160
165
|
}
|
|
161
166
|
console.log(`[fastgrc] exec-approvals server listening on ${sockPath}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastgrc-openclaw",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.33",
|
|
4
4
|
"description": "FastGRC agent compliance plugin for OpenClaw — evaluates every tool call against your policy before it executes",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|