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 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, token: configToken } = cfg.socket;
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.destroy();
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.destroy();
136
+ conn.end();
137
137
  return;
138
138
  }
139
- if (msg.type !== "request" || msg.token !== configToken) {
140
- conn.destroy();
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.write(JSON.stringify({ type: "decision", decision }) + "\n");
161
- conn.destroy();
167
+ conn.end(JSON.stringify({ type: "decision", decision }) + "\n");
162
168
  }).catch(() => {
163
- conn.write(JSON.stringify({ type: "decision", decision: "allow-once" }) + "\n");
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, 384);
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: "deny",
327
+ security: "ask",
305
328
  ask: "always",
306
- askFallback: "deny",
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
- writeConfig({ ...readConfig(), policyId: arg });
349
- process.stdout.write(`\u2713 FastGRC policy ID saved to ${CONFIG_PATH}
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
- process.stdout.write(' Run "fastgrc-hook get-policy" to verify.\n');
352
- process.exit(0);
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
- writeConfig({ ...readConfig(), apiKey: apiKeyArg });
400
- process.stdout.write("\u2713 API key saved to ~/.fastgrc.json\n");
401
- if (policyIdArg) {
402
- writeConfig({ ...readConfig(), policyId: policyIdArg });
403
- process.stdout.write("\u2713 Policy ID saved to ~/.fastgrc.json\n");
404
- } else {
405
- process.stdout.write(" (no policy ID \u2014 org-wide default will be used)\n");
406
- }
407
- doInstallHook(process.cwd());
408
- doConfigureExecApprovals();
409
- process.stdout.write("\n\u2713 Config, HOOK.md, and exec-approvals done.\n");
410
- process.stdout.write('Restart OpenClaw, then run "fastgrc-hook test" to verify.\n');
411
- process.exit(0);
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, token: configToken } = cfg.socket;
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.destroy();
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.destroy();
113
+ conn.end();
114
114
  return;
115
115
  }
116
- if (msg.type !== "request" || msg.token !== configToken) {
117
- conn.destroy();
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.write(JSON.stringify({ type: "decision", decision }) + "\n");
138
- conn.destroy();
144
+ conn.end(JSON.stringify({ type: "decision", decision }) + "\n");
139
145
  }).catch(() => {
140
- conn.write(JSON.stringify({ type: "decision", decision: "allow-once" }) + "\n");
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, 384);
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: "deny",
304
+ security: "ask",
282
305
  ask: "always",
283
- askFallback: "deny",
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
- writeConfig({ ...readConfig(), policyId: arg });
326
- process.stdout.write(`\u2713 FastGRC policy ID saved to ${CONFIG_PATH}
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
- process.stdout.write(' Run "fastgrc-hook get-policy" to verify.\n');
329
- process.exit(0);
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
- writeConfig({ ...readConfig(), apiKey: apiKeyArg });
377
- process.stdout.write("\u2713 API key saved to ~/.fastgrc.json\n");
378
- if (policyIdArg) {
379
- writeConfig({ ...readConfig(), policyId: policyIdArg });
380
- process.stdout.write("\u2713 Policy ID saved to ~/.fastgrc.json\n");
381
- } else {
382
- process.stdout.write(" (no policy ID \u2014 org-wide default will be used)\n");
383
- }
384
- doInstallHook(process.cwd());
385
- doConfigureExecApprovals();
386
- process.stdout.write("\n\u2713 Config, HOOK.md, and exec-approvals done.\n");
387
- process.stdout.write('Restart OpenClaw, then run "fastgrc-hook test" to verify.\n');
388
- process.exit(0);
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, token: configToken } = cfg.socket;
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.destroy();
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.destroy();
158
+ conn.end();
159
159
  return;
160
160
  }
161
- if (msg.type !== "request" || msg.token !== configToken) {
162
- conn.destroy();
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.write(JSON.stringify({ type: "decision", decision }) + "\n");
183
- conn.destroy();
189
+ conn.end(JSON.stringify({ type: "decision", decision }) + "\n");
184
190
  }).catch(() => {
185
- conn.write(JSON.stringify({ type: "decision", decision: "allow-once" }) + "\n");
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, 384);
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, token: configToken } = cfg.socket;
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.destroy();
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.destroy();
122
+ conn.end();
123
123
  return;
124
124
  }
125
- if (msg.type !== "request" || msg.token !== configToken) {
126
- conn.destroy();
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.write(JSON.stringify({ type: "decision", decision }) + "\n");
147
- conn.destroy();
153
+ conn.end(JSON.stringify({ type: "decision", decision }) + "\n");
148
154
  }).catch(() => {
149
- conn.write(JSON.stringify({ type: "decision", decision: "allow-once" }) + "\n");
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, 384);
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.30",
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",