fastgrc-openclaw 1.0.29 → 1.0.30
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 +60 -53
- package/dist/bin.mjs +60 -53
- package/dist/plugin.d.mts +2 -2
- package/dist/plugin.d.ts +2 -2
- package/dist/plugin.js +62 -55
- package/dist/plugin.mjs +61 -54
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -95,26 +95,9 @@ args: ${JSON.stringify(args)}`;
|
|
|
95
95
|
|
|
96
96
|
// src/plugin.ts
|
|
97
97
|
var net = __toESM(require("net"));
|
|
98
|
-
var crypto = __toESM(require("crypto"));
|
|
99
98
|
var fs2 = __toESM(require("fs"));
|
|
100
99
|
var os2 = __toESM(require("os"));
|
|
101
100
|
var path2 = __toESM(require("path"));
|
|
102
|
-
function sign(payload, token) {
|
|
103
|
-
const nonce = crypto.randomBytes(16).toString("hex");
|
|
104
|
-
const ts = Math.floor(Date.now() / 1e3).toString();
|
|
105
|
-
const body = JSON.stringify(payload);
|
|
106
|
-
const mac = crypto.createHmac("sha256", Buffer.from(token, "base64")).update(`${nonce}:${ts}:${body}`).digest("hex");
|
|
107
|
-
return { ...payload, nonce, timestamp: ts, hmac: mac };
|
|
108
|
-
}
|
|
109
|
-
function verifyHmac(req, token) {
|
|
110
|
-
try {
|
|
111
|
-
const { nonce, timestamp, hmac, ...rest } = req;
|
|
112
|
-
const expected = crypto.createHmac("sha256", Buffer.from(token, "base64")).update(`${nonce}:${timestamp}:${JSON.stringify(rest)}`).digest("hex");
|
|
113
|
-
return hmac === expected;
|
|
114
|
-
} catch {
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
101
|
function readExecApprovalsConfig() {
|
|
119
102
|
const cfgPath = path2.join(os2.homedir(), ".openclaw", "exec-approvals.json");
|
|
120
103
|
try {
|
|
@@ -123,53 +106,77 @@ function readExecApprovalsConfig() {
|
|
|
123
106
|
return null;
|
|
124
107
|
}
|
|
125
108
|
}
|
|
126
|
-
function
|
|
109
|
+
function startExecApprovalsServer(apiKey, policyId, baseUrl) {
|
|
127
110
|
const cfg = readExecApprovalsConfig();
|
|
128
|
-
if (!cfg?.socket?.path || !cfg?.socket?.token)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
111
|
+
if (!cfg?.socket?.path || !cfg?.socket?.token) {
|
|
112
|
+
console.warn("[fastgrc] exec-approvals: no socket config found \u2014 webchat execs will not be evaluated");
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const { path: sockPath, token: configToken } = cfg.socket;
|
|
116
|
+
try {
|
|
117
|
+
fs2.unlinkSync(sockPath);
|
|
118
|
+
} catch {
|
|
119
|
+
}
|
|
120
|
+
const server = net.createServer((conn) => {
|
|
132
121
|
let buffer = "";
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
122
|
+
conn.on("data", (chunk) => {
|
|
123
|
+
buffer += chunk.toString("utf8");
|
|
124
|
+
const idx = buffer.indexOf("\n");
|
|
125
|
+
if (idx === -1) return;
|
|
126
|
+
const line = buffer.slice(0, idx).trim();
|
|
127
|
+
buffer = "";
|
|
128
|
+
if (!line) {
|
|
129
|
+
conn.destroy();
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
let msg;
|
|
139
133
|
try {
|
|
140
|
-
|
|
134
|
+
msg = JSON.parse(line);
|
|
141
135
|
} catch {
|
|
136
|
+
conn.destroy();
|
|
142
137
|
return;
|
|
143
138
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
console.warn("[fastgrc] exec-approvals: HMAC verification failed \u2014 ignoring request");
|
|
139
|
+
if (msg.type !== "request" || msg.token !== configToken) {
|
|
140
|
+
conn.destroy();
|
|
147
141
|
return;
|
|
148
142
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
143
|
+
const req = msg.request ?? {};
|
|
144
|
+
const command = String(req.command ?? req.commandPreview ?? "unknown");
|
|
145
|
+
const agentId = req.agentId || void 0;
|
|
146
|
+
evaluate({
|
|
147
|
+
toolName: "Exec",
|
|
148
|
+
args: { command, cwd: req.cwd },
|
|
149
|
+
agentId,
|
|
150
|
+
apiKey,
|
|
151
|
+
policyId,
|
|
152
|
+
baseUrl
|
|
153
|
+
}).then((result) => {
|
|
154
|
+
let decision = "allow-once";
|
|
159
155
|
if (result && (result.decision === "block" || result.decision === "require_approval")) {
|
|
160
156
|
decision = "deny";
|
|
161
|
-
const
|
|
162
|
-
console.warn(
|
|
157
|
+
const rule = result.policyContext?.matchedRule;
|
|
158
|
+
console.warn(rule ? `[fastgrc] exec blocked: [${rule}] ${result.reasoning}` : `[fastgrc] exec blocked: ${result.reasoning}`);
|
|
163
159
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
160
|
+
conn.write(JSON.stringify({ type: "decision", decision }) + "\n");
|
|
161
|
+
conn.destroy();
|
|
162
|
+
}).catch(() => {
|
|
163
|
+
conn.write(JSON.stringify({ type: "decision", decision: "allow-once" }) + "\n");
|
|
164
|
+
conn.destroy();
|
|
165
|
+
});
|
|
168
166
|
});
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
|
|
167
|
+
conn.on("error", () => {
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
server.listen(sockPath, () => {
|
|
171
|
+
try {
|
|
172
|
+
fs2.chmodSync(sockPath, 384);
|
|
173
|
+
} catch {
|
|
174
|
+
}
|
|
175
|
+
console.log(`[fastgrc] exec-approvals server listening on ${sockPath}`);
|
|
176
|
+
});
|
|
177
|
+
server.on("error", (err) => {
|
|
178
|
+
console.warn(`[fastgrc] exec-approvals server error: ${err.message}`);
|
|
179
|
+
});
|
|
173
180
|
}
|
|
174
181
|
|
|
175
182
|
// src/bin.ts
|
|
@@ -420,7 +427,7 @@ if (cmd === "serve-approvals") {
|
|
|
420
427
|
const baseUrl = process.env.FASTGRC_BASE_URL ?? "https://app.fastgrc.ai";
|
|
421
428
|
process.stdout.write(`[fastgrc] serve-approvals running \u2014 listening on ${cfg.socket.path}
|
|
422
429
|
`);
|
|
423
|
-
|
|
430
|
+
startExecApprovalsServer(apiKey, policyId, baseUrl);
|
|
424
431
|
}
|
|
425
432
|
if (cmd === "uninstall") {
|
|
426
433
|
const targetDir = arg || process.cwd();
|
package/dist/bin.mjs
CHANGED
|
@@ -72,26 +72,9 @@ args: ${JSON.stringify(args)}`;
|
|
|
72
72
|
|
|
73
73
|
// src/plugin.ts
|
|
74
74
|
import * as net from "net";
|
|
75
|
-
import * as crypto from "crypto";
|
|
76
75
|
import * as fs2 from "fs";
|
|
77
76
|
import * as os2 from "os";
|
|
78
77
|
import * as path2 from "path";
|
|
79
|
-
function sign(payload, token) {
|
|
80
|
-
const nonce = crypto.randomBytes(16).toString("hex");
|
|
81
|
-
const ts = Math.floor(Date.now() / 1e3).toString();
|
|
82
|
-
const body = JSON.stringify(payload);
|
|
83
|
-
const mac = crypto.createHmac("sha256", Buffer.from(token, "base64")).update(`${nonce}:${ts}:${body}`).digest("hex");
|
|
84
|
-
return { ...payload, nonce, timestamp: ts, hmac: mac };
|
|
85
|
-
}
|
|
86
|
-
function verifyHmac(req, token) {
|
|
87
|
-
try {
|
|
88
|
-
const { nonce, timestamp, hmac, ...rest } = req;
|
|
89
|
-
const expected = crypto.createHmac("sha256", Buffer.from(token, "base64")).update(`${nonce}:${timestamp}:${JSON.stringify(rest)}`).digest("hex");
|
|
90
|
-
return hmac === expected;
|
|
91
|
-
} catch {
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
78
|
function readExecApprovalsConfig() {
|
|
96
79
|
const cfgPath = path2.join(os2.homedir(), ".openclaw", "exec-approvals.json");
|
|
97
80
|
try {
|
|
@@ -100,53 +83,77 @@ function readExecApprovalsConfig() {
|
|
|
100
83
|
return null;
|
|
101
84
|
}
|
|
102
85
|
}
|
|
103
|
-
function
|
|
86
|
+
function startExecApprovalsServer(apiKey, policyId, baseUrl) {
|
|
104
87
|
const cfg = readExecApprovalsConfig();
|
|
105
|
-
if (!cfg?.socket?.path || !cfg?.socket?.token)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
88
|
+
if (!cfg?.socket?.path || !cfg?.socket?.token) {
|
|
89
|
+
console.warn("[fastgrc] exec-approvals: no socket config found \u2014 webchat execs will not be evaluated");
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const { path: sockPath, token: configToken } = cfg.socket;
|
|
93
|
+
try {
|
|
94
|
+
fs2.unlinkSync(sockPath);
|
|
95
|
+
} catch {
|
|
96
|
+
}
|
|
97
|
+
const server = net.createServer((conn) => {
|
|
109
98
|
let buffer = "";
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
99
|
+
conn.on("data", (chunk) => {
|
|
100
|
+
buffer += chunk.toString("utf8");
|
|
101
|
+
const idx = buffer.indexOf("\n");
|
|
102
|
+
if (idx === -1) return;
|
|
103
|
+
const line = buffer.slice(0, idx).trim();
|
|
104
|
+
buffer = "";
|
|
105
|
+
if (!line) {
|
|
106
|
+
conn.destroy();
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
let msg;
|
|
116
110
|
try {
|
|
117
|
-
|
|
111
|
+
msg = JSON.parse(line);
|
|
118
112
|
} catch {
|
|
113
|
+
conn.destroy();
|
|
119
114
|
return;
|
|
120
115
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
console.warn("[fastgrc] exec-approvals: HMAC verification failed \u2014 ignoring request");
|
|
116
|
+
if (msg.type !== "request" || msg.token !== configToken) {
|
|
117
|
+
conn.destroy();
|
|
124
118
|
return;
|
|
125
119
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
120
|
+
const req = msg.request ?? {};
|
|
121
|
+
const command = String(req.command ?? req.commandPreview ?? "unknown");
|
|
122
|
+
const agentId = req.agentId || void 0;
|
|
123
|
+
evaluate({
|
|
124
|
+
toolName: "Exec",
|
|
125
|
+
args: { command, cwd: req.cwd },
|
|
126
|
+
agentId,
|
|
127
|
+
apiKey,
|
|
128
|
+
policyId,
|
|
129
|
+
baseUrl
|
|
130
|
+
}).then((result) => {
|
|
131
|
+
let decision = "allow-once";
|
|
136
132
|
if (result && (result.decision === "block" || result.decision === "require_approval")) {
|
|
137
133
|
decision = "deny";
|
|
138
|
-
const
|
|
139
|
-
console.warn(
|
|
134
|
+
const rule = result.policyContext?.matchedRule;
|
|
135
|
+
console.warn(rule ? `[fastgrc] exec blocked: [${rule}] ${result.reasoning}` : `[fastgrc] exec blocked: ${result.reasoning}`);
|
|
140
136
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
137
|
+
conn.write(JSON.stringify({ type: "decision", decision }) + "\n");
|
|
138
|
+
conn.destroy();
|
|
139
|
+
}).catch(() => {
|
|
140
|
+
conn.write(JSON.stringify({ type: "decision", decision: "allow-once" }) + "\n");
|
|
141
|
+
conn.destroy();
|
|
142
|
+
});
|
|
145
143
|
});
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
|
|
144
|
+
conn.on("error", () => {
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
server.listen(sockPath, () => {
|
|
148
|
+
try {
|
|
149
|
+
fs2.chmodSync(sockPath, 384);
|
|
150
|
+
} catch {
|
|
151
|
+
}
|
|
152
|
+
console.log(`[fastgrc] exec-approvals server listening on ${sockPath}`);
|
|
153
|
+
});
|
|
154
|
+
server.on("error", (err) => {
|
|
155
|
+
console.warn(`[fastgrc] exec-approvals server error: ${err.message}`);
|
|
156
|
+
});
|
|
150
157
|
}
|
|
151
158
|
|
|
152
159
|
// src/bin.ts
|
|
@@ -397,7 +404,7 @@ if (cmd === "serve-approvals") {
|
|
|
397
404
|
const baseUrl = process.env.FASTGRC_BASE_URL ?? "https://app.fastgrc.ai";
|
|
398
405
|
process.stdout.write(`[fastgrc] serve-approvals running \u2014 listening on ${cfg.socket.path}
|
|
399
406
|
`);
|
|
400
|
-
|
|
407
|
+
startExecApprovalsServer(apiKey, policyId, baseUrl);
|
|
401
408
|
}
|
|
402
409
|
if (cmd === "uninstall") {
|
|
403
410
|
const targetDir = arg || process.cwd();
|
package/dist/plugin.d.mts
CHANGED
|
@@ -5,7 +5,7 @@ interface ExecApprovalsConfig {
|
|
|
5
5
|
};
|
|
6
6
|
}
|
|
7
7
|
declare function readExecApprovalsConfig(): ExecApprovalsConfig | null;
|
|
8
|
-
declare function
|
|
8
|
+
declare function startExecApprovalsServer(apiKey: string, policyId?: string, baseUrl?: string): void;
|
|
9
9
|
declare const pluginEntry: {
|
|
10
10
|
id: string;
|
|
11
11
|
name: string;
|
|
@@ -13,4 +13,4 @@ declare const pluginEntry: {
|
|
|
13
13
|
register(api: any): void;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
export { pluginEntry as default, readExecApprovalsConfig,
|
|
16
|
+
export { pluginEntry as default, readExecApprovalsConfig, startExecApprovalsServer };
|
package/dist/plugin.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ interface ExecApprovalsConfig {
|
|
|
5
5
|
};
|
|
6
6
|
}
|
|
7
7
|
declare function readExecApprovalsConfig(): ExecApprovalsConfig | null;
|
|
8
|
-
declare function
|
|
8
|
+
declare function startExecApprovalsServer(apiKey: string, policyId?: string, baseUrl?: string): void;
|
|
9
9
|
declare const pluginEntry: {
|
|
10
10
|
id: string;
|
|
11
11
|
name: string;
|
|
@@ -13,4 +13,4 @@ declare const pluginEntry: {
|
|
|
13
13
|
register(api: any): void;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
export { pluginEntry as default, readExecApprovalsConfig,
|
|
16
|
+
export { pluginEntry as default, readExecApprovalsConfig, startExecApprovalsServer };
|
package/dist/plugin.js
CHANGED
|
@@ -32,11 +32,10 @@ var plugin_exports = {};
|
|
|
32
32
|
__export(plugin_exports, {
|
|
33
33
|
default: () => plugin_default,
|
|
34
34
|
readExecApprovalsConfig: () => readExecApprovalsConfig,
|
|
35
|
-
|
|
35
|
+
startExecApprovalsServer: () => startExecApprovalsServer
|
|
36
36
|
});
|
|
37
37
|
module.exports = __toCommonJS(plugin_exports);
|
|
38
38
|
var net = __toESM(require("net"));
|
|
39
|
-
var crypto = __toESM(require("crypto"));
|
|
40
39
|
var fs2 = __toESM(require("fs"));
|
|
41
40
|
var os2 = __toESM(require("os"));
|
|
42
41
|
var path2 = __toESM(require("path"));
|
|
@@ -121,22 +120,6 @@ args: ${JSON.stringify(args)}`;
|
|
|
121
120
|
// src/plugin.ts
|
|
122
121
|
var DEFAULT_BASE_URL2 = "https://app.fastgrc.ai";
|
|
123
122
|
var DEFAULT_TIMEOUT_MS2 = 3e3;
|
|
124
|
-
function sign(payload, token) {
|
|
125
|
-
const nonce = crypto.randomBytes(16).toString("hex");
|
|
126
|
-
const ts = Math.floor(Date.now() / 1e3).toString();
|
|
127
|
-
const body = JSON.stringify(payload);
|
|
128
|
-
const mac = crypto.createHmac("sha256", Buffer.from(token, "base64")).update(`${nonce}:${ts}:${body}`).digest("hex");
|
|
129
|
-
return { ...payload, nonce, timestamp: ts, hmac: mac };
|
|
130
|
-
}
|
|
131
|
-
function verifyHmac(req, token) {
|
|
132
|
-
try {
|
|
133
|
-
const { nonce, timestamp, hmac, ...rest } = req;
|
|
134
|
-
const expected = crypto.createHmac("sha256", Buffer.from(token, "base64")).update(`${nonce}:${timestamp}:${JSON.stringify(rest)}`).digest("hex");
|
|
135
|
-
return hmac === expected;
|
|
136
|
-
} catch {
|
|
137
|
-
return false;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
123
|
function readExecApprovalsConfig() {
|
|
141
124
|
const cfgPath = path2.join(os2.homedir(), ".openclaw", "exec-approvals.json");
|
|
142
125
|
try {
|
|
@@ -145,53 +128,77 @@ function readExecApprovalsConfig() {
|
|
|
145
128
|
return null;
|
|
146
129
|
}
|
|
147
130
|
}
|
|
148
|
-
function
|
|
131
|
+
function startExecApprovalsServer(apiKey, policyId, baseUrl) {
|
|
149
132
|
const cfg = readExecApprovalsConfig();
|
|
150
|
-
if (!cfg?.socket?.path || !cfg?.socket?.token)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
133
|
+
if (!cfg?.socket?.path || !cfg?.socket?.token) {
|
|
134
|
+
console.warn("[fastgrc] exec-approvals: no socket config found \u2014 webchat execs will not be evaluated");
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const { path: sockPath, token: configToken } = cfg.socket;
|
|
138
|
+
try {
|
|
139
|
+
fs2.unlinkSync(sockPath);
|
|
140
|
+
} catch {
|
|
141
|
+
}
|
|
142
|
+
const server = net.createServer((conn) => {
|
|
154
143
|
let buffer = "";
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
144
|
+
conn.on("data", (chunk) => {
|
|
145
|
+
buffer += chunk.toString("utf8");
|
|
146
|
+
const idx = buffer.indexOf("\n");
|
|
147
|
+
if (idx === -1) return;
|
|
148
|
+
const line = buffer.slice(0, idx).trim();
|
|
149
|
+
buffer = "";
|
|
150
|
+
if (!line) {
|
|
151
|
+
conn.destroy();
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
let msg;
|
|
161
155
|
try {
|
|
162
|
-
|
|
156
|
+
msg = JSON.parse(line);
|
|
163
157
|
} catch {
|
|
158
|
+
conn.destroy();
|
|
164
159
|
return;
|
|
165
160
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
console.warn("[fastgrc] exec-approvals: HMAC verification failed \u2014 ignoring request");
|
|
161
|
+
if (msg.type !== "request" || msg.token !== configToken) {
|
|
162
|
+
conn.destroy();
|
|
169
163
|
return;
|
|
170
164
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
165
|
+
const req = msg.request ?? {};
|
|
166
|
+
const command = String(req.command ?? req.commandPreview ?? "unknown");
|
|
167
|
+
const agentId = req.agentId || void 0;
|
|
168
|
+
evaluate({
|
|
169
|
+
toolName: "Exec",
|
|
170
|
+
args: { command, cwd: req.cwd },
|
|
171
|
+
agentId,
|
|
172
|
+
apiKey,
|
|
173
|
+
policyId,
|
|
174
|
+
baseUrl
|
|
175
|
+
}).then((result) => {
|
|
176
|
+
let decision = "allow-once";
|
|
181
177
|
if (result && (result.decision === "block" || result.decision === "require_approval")) {
|
|
182
178
|
decision = "deny";
|
|
183
|
-
const
|
|
184
|
-
console.warn(
|
|
179
|
+
const rule = result.policyContext?.matchedRule;
|
|
180
|
+
console.warn(rule ? `[fastgrc] exec blocked: [${rule}] ${result.reasoning}` : `[fastgrc] exec blocked: ${result.reasoning}`);
|
|
185
181
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
182
|
+
conn.write(JSON.stringify({ type: "decision", decision }) + "\n");
|
|
183
|
+
conn.destroy();
|
|
184
|
+
}).catch(() => {
|
|
185
|
+
conn.write(JSON.stringify({ type: "decision", decision: "allow-once" }) + "\n");
|
|
186
|
+
conn.destroy();
|
|
187
|
+
});
|
|
190
188
|
});
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
|
|
189
|
+
conn.on("error", () => {
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
server.listen(sockPath, () => {
|
|
193
|
+
try {
|
|
194
|
+
fs2.chmodSync(sockPath, 384);
|
|
195
|
+
} catch {
|
|
196
|
+
}
|
|
197
|
+
console.log(`[fastgrc] exec-approvals server listening on ${sockPath}`);
|
|
198
|
+
});
|
|
199
|
+
server.on("error", (err) => {
|
|
200
|
+
console.warn(`[fastgrc] exec-approvals server error: ${err.message}`);
|
|
201
|
+
});
|
|
195
202
|
}
|
|
196
203
|
var pluginEntry = {
|
|
197
204
|
id: "fastgrc",
|
|
@@ -237,13 +244,13 @@ var pluginEntry = {
|
|
|
237
244
|
}
|
|
238
245
|
return void 0;
|
|
239
246
|
});
|
|
240
|
-
|
|
247
|
+
startExecApprovalsServer(apiKey, policyId, DEFAULT_BASE_URL2);
|
|
241
248
|
}
|
|
242
249
|
};
|
|
243
250
|
var plugin_default = pluginEntry;
|
|
244
251
|
// Annotate the CommonJS export names for ESM import in node:
|
|
245
252
|
0 && (module.exports = {
|
|
246
253
|
readExecApprovalsConfig,
|
|
247
|
-
|
|
254
|
+
startExecApprovalsServer
|
|
248
255
|
});
|
|
249
256
|
module.exports = module.exports.default ?? module.exports;
|
package/dist/plugin.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// src/plugin.ts
|
|
2
2
|
import * as net from "net";
|
|
3
|
-
import * as crypto from "crypto";
|
|
4
3
|
import * as fs2 from "fs";
|
|
5
4
|
import * as os2 from "os";
|
|
6
5
|
import * as path2 from "path";
|
|
@@ -85,22 +84,6 @@ args: ${JSON.stringify(args)}`;
|
|
|
85
84
|
// src/plugin.ts
|
|
86
85
|
var DEFAULT_BASE_URL2 = "https://app.fastgrc.ai";
|
|
87
86
|
var DEFAULT_TIMEOUT_MS2 = 3e3;
|
|
88
|
-
function sign(payload, token) {
|
|
89
|
-
const nonce = crypto.randomBytes(16).toString("hex");
|
|
90
|
-
const ts = Math.floor(Date.now() / 1e3).toString();
|
|
91
|
-
const body = JSON.stringify(payload);
|
|
92
|
-
const mac = crypto.createHmac("sha256", Buffer.from(token, "base64")).update(`${nonce}:${ts}:${body}`).digest("hex");
|
|
93
|
-
return { ...payload, nonce, timestamp: ts, hmac: mac };
|
|
94
|
-
}
|
|
95
|
-
function verifyHmac(req, token) {
|
|
96
|
-
try {
|
|
97
|
-
const { nonce, timestamp, hmac, ...rest } = req;
|
|
98
|
-
const expected = crypto.createHmac("sha256", Buffer.from(token, "base64")).update(`${nonce}:${timestamp}:${JSON.stringify(rest)}`).digest("hex");
|
|
99
|
-
return hmac === expected;
|
|
100
|
-
} catch {
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
87
|
function readExecApprovalsConfig() {
|
|
105
88
|
const cfgPath = path2.join(os2.homedir(), ".openclaw", "exec-approvals.json");
|
|
106
89
|
try {
|
|
@@ -109,53 +92,77 @@ function readExecApprovalsConfig() {
|
|
|
109
92
|
return null;
|
|
110
93
|
}
|
|
111
94
|
}
|
|
112
|
-
function
|
|
95
|
+
function startExecApprovalsServer(apiKey, policyId, baseUrl) {
|
|
113
96
|
const cfg = readExecApprovalsConfig();
|
|
114
|
-
if (!cfg?.socket?.path || !cfg?.socket?.token)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
97
|
+
if (!cfg?.socket?.path || !cfg?.socket?.token) {
|
|
98
|
+
console.warn("[fastgrc] exec-approvals: no socket config found \u2014 webchat execs will not be evaluated");
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const { path: sockPath, token: configToken } = cfg.socket;
|
|
102
|
+
try {
|
|
103
|
+
fs2.unlinkSync(sockPath);
|
|
104
|
+
} catch {
|
|
105
|
+
}
|
|
106
|
+
const server = net.createServer((conn) => {
|
|
118
107
|
let buffer = "";
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
108
|
+
conn.on("data", (chunk) => {
|
|
109
|
+
buffer += chunk.toString("utf8");
|
|
110
|
+
const idx = buffer.indexOf("\n");
|
|
111
|
+
if (idx === -1) return;
|
|
112
|
+
const line = buffer.slice(0, idx).trim();
|
|
113
|
+
buffer = "";
|
|
114
|
+
if (!line) {
|
|
115
|
+
conn.destroy();
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
let msg;
|
|
125
119
|
try {
|
|
126
|
-
|
|
120
|
+
msg = JSON.parse(line);
|
|
127
121
|
} catch {
|
|
122
|
+
conn.destroy();
|
|
128
123
|
return;
|
|
129
124
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
console.warn("[fastgrc] exec-approvals: HMAC verification failed \u2014 ignoring request");
|
|
125
|
+
if (msg.type !== "request" || msg.token !== configToken) {
|
|
126
|
+
conn.destroy();
|
|
133
127
|
return;
|
|
134
128
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
129
|
+
const req = msg.request ?? {};
|
|
130
|
+
const command = String(req.command ?? req.commandPreview ?? "unknown");
|
|
131
|
+
const agentId = req.agentId || void 0;
|
|
132
|
+
evaluate({
|
|
133
|
+
toolName: "Exec",
|
|
134
|
+
args: { command, cwd: req.cwd },
|
|
135
|
+
agentId,
|
|
136
|
+
apiKey,
|
|
137
|
+
policyId,
|
|
138
|
+
baseUrl
|
|
139
|
+
}).then((result) => {
|
|
140
|
+
let decision = "allow-once";
|
|
145
141
|
if (result && (result.decision === "block" || result.decision === "require_approval")) {
|
|
146
142
|
decision = "deny";
|
|
147
|
-
const
|
|
148
|
-
console.warn(
|
|
143
|
+
const rule = result.policyContext?.matchedRule;
|
|
144
|
+
console.warn(rule ? `[fastgrc] exec blocked: [${rule}] ${result.reasoning}` : `[fastgrc] exec blocked: ${result.reasoning}`);
|
|
149
145
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
146
|
+
conn.write(JSON.stringify({ type: "decision", decision }) + "\n");
|
|
147
|
+
conn.destroy();
|
|
148
|
+
}).catch(() => {
|
|
149
|
+
conn.write(JSON.stringify({ type: "decision", decision: "allow-once" }) + "\n");
|
|
150
|
+
conn.destroy();
|
|
151
|
+
});
|
|
154
152
|
});
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
|
|
153
|
+
conn.on("error", () => {
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
server.listen(sockPath, () => {
|
|
157
|
+
try {
|
|
158
|
+
fs2.chmodSync(sockPath, 384);
|
|
159
|
+
} catch {
|
|
160
|
+
}
|
|
161
|
+
console.log(`[fastgrc] exec-approvals server listening on ${sockPath}`);
|
|
162
|
+
});
|
|
163
|
+
server.on("error", (err) => {
|
|
164
|
+
console.warn(`[fastgrc] exec-approvals server error: ${err.message}`);
|
|
165
|
+
});
|
|
159
166
|
}
|
|
160
167
|
var pluginEntry = {
|
|
161
168
|
id: "fastgrc",
|
|
@@ -201,12 +208,12 @@ var pluginEntry = {
|
|
|
201
208
|
}
|
|
202
209
|
return void 0;
|
|
203
210
|
});
|
|
204
|
-
|
|
211
|
+
startExecApprovalsServer(apiKey, policyId, DEFAULT_BASE_URL2);
|
|
205
212
|
}
|
|
206
213
|
};
|
|
207
214
|
var plugin_default = pluginEntry;
|
|
208
215
|
export {
|
|
209
216
|
plugin_default as default,
|
|
210
217
|
readExecApprovalsConfig,
|
|
211
|
-
|
|
218
|
+
startExecApprovalsServer
|
|
212
219
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastgrc-openclaw",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.30",
|
|
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",
|