@agenshield/daemon 0.5.0 → 0.6.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/acl.d.ts.map +1 -1
- package/command-sync.d.ts +1 -1
- package/command-sync.d.ts.map +1 -1
- package/index.js +630 -309
- package/main.js +653 -313
- package/package.json +4 -4
- package/routes/config.d.ts.map +1 -1
- package/routes/rpc.d.ts.map +1 -1
- package/server.d.ts.map +1 -1
- package/ui-assets/assets/{index-C4yZ-JLI.js → index-DRQK9Oxg.js} +224 -224
- package/ui-assets/index.html +1 -1
package/main.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res) => function __init() {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
};
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
5
11
|
|
|
6
12
|
// libs/shield-daemon/src/config/paths.ts
|
|
7
13
|
import * as path from "node:path";
|
|
@@ -16,10 +22,14 @@ function getConfigPath() {
|
|
|
16
22
|
function getPidPath() {
|
|
17
23
|
return path.join(getConfigDir(), PID_FILE);
|
|
18
24
|
}
|
|
25
|
+
var init_paths = __esm({
|
|
26
|
+
"libs/shield-daemon/src/config/paths.ts"() {
|
|
27
|
+
"use strict";
|
|
28
|
+
}
|
|
29
|
+
});
|
|
19
30
|
|
|
20
31
|
// libs/shield-daemon/src/config/defaults.ts
|
|
21
32
|
import { DEFAULT_PORT, DEFAULT_HOST } from "@agenshield/ipc";
|
|
22
|
-
var VERSION = "0.1.0";
|
|
23
33
|
function getDefaultConfig() {
|
|
24
34
|
return {
|
|
25
35
|
version: VERSION,
|
|
@@ -36,8 +46,22 @@ function getDefaultConfig() {
|
|
|
36
46
|
}
|
|
37
47
|
};
|
|
38
48
|
}
|
|
49
|
+
var VERSION;
|
|
50
|
+
var init_defaults = __esm({
|
|
51
|
+
"libs/shield-daemon/src/config/defaults.ts"() {
|
|
52
|
+
"use strict";
|
|
53
|
+
VERSION = "0.1.0";
|
|
54
|
+
}
|
|
55
|
+
});
|
|
39
56
|
|
|
40
57
|
// libs/shield-daemon/src/config/loader.ts
|
|
58
|
+
var loader_exports = {};
|
|
59
|
+
__export(loader_exports, {
|
|
60
|
+
ensureConfigDir: () => ensureConfigDir,
|
|
61
|
+
loadConfig: () => loadConfig,
|
|
62
|
+
saveConfig: () => saveConfig,
|
|
63
|
+
updateConfig: () => updateConfig
|
|
64
|
+
});
|
|
41
65
|
import * as fs from "node:fs";
|
|
42
66
|
import { ShieldConfigSchema, DEFAULT_PORT as DEFAULT_PORT2 } from "@agenshield/ipc";
|
|
43
67
|
function ensureConfigDir() {
|
|
@@ -84,31 +108,35 @@ function updateConfig(updates) {
|
|
|
84
108
|
saveConfig(updated);
|
|
85
109
|
return updated;
|
|
86
110
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
// libs/shield-daemon/src/routes/index.ts
|
|
95
|
-
import { API_PREFIX } from "@agenshield/ipc";
|
|
96
|
-
|
|
97
|
-
// libs/shield-daemon/src/routes/health.ts
|
|
98
|
-
async function healthRoutes(app) {
|
|
99
|
-
app.get("/health", async () => {
|
|
100
|
-
return {
|
|
101
|
-
success: true,
|
|
102
|
-
data: {
|
|
103
|
-
ok: true,
|
|
104
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
105
|
-
mode: "daemon"
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
});
|
|
109
|
-
}
|
|
111
|
+
var init_loader = __esm({
|
|
112
|
+
"libs/shield-daemon/src/config/loader.ts"() {
|
|
113
|
+
"use strict";
|
|
114
|
+
init_paths();
|
|
115
|
+
init_defaults();
|
|
116
|
+
}
|
|
117
|
+
});
|
|
110
118
|
|
|
111
119
|
// libs/shield-daemon/src/state/index.ts
|
|
120
|
+
var state_exports = {};
|
|
121
|
+
__export(state_exports, {
|
|
122
|
+
addConnectedIntegration: () => addConnectedIntegration,
|
|
123
|
+
addGroupState: () => addGroupState,
|
|
124
|
+
addUserState: () => addUserState,
|
|
125
|
+
getDefaultState: () => getDefaultState,
|
|
126
|
+
getPasscodeProtectionState: () => getPasscodeProtectionState,
|
|
127
|
+
getStatePath: () => getStatePath,
|
|
128
|
+
initializeState: () => initializeState,
|
|
129
|
+
loadState: () => loadState,
|
|
130
|
+
removeConnectedIntegration: () => removeConnectedIntegration,
|
|
131
|
+
removeGroupState: () => removeGroupState,
|
|
132
|
+
removeUserState: () => removeUserState,
|
|
133
|
+
saveState: () => saveState,
|
|
134
|
+
updateAgenCoState: () => updateAgenCoState,
|
|
135
|
+
updateDaemonState: () => updateDaemonState,
|
|
136
|
+
updateInstallationState: () => updateInstallationState,
|
|
137
|
+
updatePasscodeProtectionState: () => updatePasscodeProtectionState,
|
|
138
|
+
updateState: () => updateState
|
|
139
|
+
});
|
|
112
140
|
import * as fs2 from "node:fs";
|
|
113
141
|
import * as path2 from "node:path";
|
|
114
142
|
import { STATE_FILE, DEFAULT_PORT as DEFAULT_PORT3 } from "@agenshield/ipc";
|
|
@@ -165,18 +193,89 @@ function saveState(state) {
|
|
|
165
193
|
}
|
|
166
194
|
fs2.writeFileSync(statePath, JSON.stringify(state, null, 2), { mode: 420 });
|
|
167
195
|
}
|
|
196
|
+
function updateState(updates) {
|
|
197
|
+
const current = loadState();
|
|
198
|
+
const updated = {
|
|
199
|
+
...current,
|
|
200
|
+
...updates
|
|
201
|
+
};
|
|
202
|
+
if (updates.daemon) {
|
|
203
|
+
updated.daemon = { ...current.daemon, ...updates.daemon };
|
|
204
|
+
}
|
|
205
|
+
if (updates.agenco) {
|
|
206
|
+
updated.agenco = { ...current.agenco, ...updates.agenco };
|
|
207
|
+
}
|
|
208
|
+
if (updates.installation) {
|
|
209
|
+
updated.installation = { ...current.installation, ...updates.installation };
|
|
210
|
+
}
|
|
211
|
+
if (updates.passcodeProtection) {
|
|
212
|
+
updated.passcodeProtection = { ...current.passcodeProtection, ...updates.passcodeProtection };
|
|
213
|
+
}
|
|
214
|
+
saveState(updated);
|
|
215
|
+
return updated;
|
|
216
|
+
}
|
|
217
|
+
function updateDaemonState(updates) {
|
|
218
|
+
const current = loadState();
|
|
219
|
+
current.daemon = { ...current.daemon, ...updates };
|
|
220
|
+
saveState(current);
|
|
221
|
+
return current;
|
|
222
|
+
}
|
|
168
223
|
function updateAgenCoState(updates) {
|
|
169
224
|
const current = loadState();
|
|
170
225
|
current.agenco = { ...current.agenco, ...updates };
|
|
171
226
|
saveState(current);
|
|
172
227
|
return current;
|
|
173
228
|
}
|
|
229
|
+
function updateInstallationState(updates) {
|
|
230
|
+
const current = loadState();
|
|
231
|
+
current.installation = { ...current.installation, ...updates };
|
|
232
|
+
saveState(current);
|
|
233
|
+
return current;
|
|
234
|
+
}
|
|
174
235
|
function updatePasscodeProtectionState(updates) {
|
|
175
236
|
const current = loadState();
|
|
176
237
|
current.passcodeProtection = { ...current.passcodeProtection, ...updates };
|
|
177
238
|
saveState(current);
|
|
178
239
|
return current;
|
|
179
240
|
}
|
|
241
|
+
function getPasscodeProtectionState() {
|
|
242
|
+
const current = loadState();
|
|
243
|
+
return current.passcodeProtection;
|
|
244
|
+
}
|
|
245
|
+
function addUserState(user) {
|
|
246
|
+
const current = loadState();
|
|
247
|
+
const existingIndex = current.users.findIndex((u) => u.username === user.username);
|
|
248
|
+
if (existingIndex >= 0) {
|
|
249
|
+
current.users[existingIndex] = user;
|
|
250
|
+
} else {
|
|
251
|
+
current.users.push(user);
|
|
252
|
+
}
|
|
253
|
+
saveState(current);
|
|
254
|
+
return current;
|
|
255
|
+
}
|
|
256
|
+
function removeUserState(username) {
|
|
257
|
+
const current = loadState();
|
|
258
|
+
current.users = current.users.filter((u) => u.username !== username);
|
|
259
|
+
saveState(current);
|
|
260
|
+
return current;
|
|
261
|
+
}
|
|
262
|
+
function addGroupState(group) {
|
|
263
|
+
const current = loadState();
|
|
264
|
+
const existingIndex = current.groups.findIndex((g) => g.name === group.name);
|
|
265
|
+
if (existingIndex >= 0) {
|
|
266
|
+
current.groups[existingIndex] = group;
|
|
267
|
+
} else {
|
|
268
|
+
current.groups.push(group);
|
|
269
|
+
}
|
|
270
|
+
saveState(current);
|
|
271
|
+
return current;
|
|
272
|
+
}
|
|
273
|
+
function removeGroupState(name) {
|
|
274
|
+
const current = loadState();
|
|
275
|
+
current.groups = current.groups.filter((g) => g.name !== name);
|
|
276
|
+
saveState(current);
|
|
277
|
+
return current;
|
|
278
|
+
}
|
|
180
279
|
function addConnectedIntegration(integrationId) {
|
|
181
280
|
const current = loadState();
|
|
182
281
|
if (!current.agenco.connectedIntegrations.includes(integrationId)) {
|
|
@@ -185,8 +284,308 @@ function addConnectedIntegration(integrationId) {
|
|
|
185
284
|
}
|
|
186
285
|
return current;
|
|
187
286
|
}
|
|
287
|
+
function removeConnectedIntegration(integrationId) {
|
|
288
|
+
const current = loadState();
|
|
289
|
+
current.agenco.connectedIntegrations = current.agenco.connectedIntegrations.filter(
|
|
290
|
+
(id) => id !== integrationId
|
|
291
|
+
);
|
|
292
|
+
saveState(current);
|
|
293
|
+
return current;
|
|
294
|
+
}
|
|
295
|
+
function initializeState() {
|
|
296
|
+
const statePath = getStatePath();
|
|
297
|
+
if (!fs2.existsSync(statePath)) {
|
|
298
|
+
const state = getDefaultState();
|
|
299
|
+
saveState(state);
|
|
300
|
+
return state;
|
|
301
|
+
}
|
|
302
|
+
return loadState();
|
|
303
|
+
}
|
|
304
|
+
var init_state = __esm({
|
|
305
|
+
"libs/shield-daemon/src/state/index.ts"() {
|
|
306
|
+
"use strict";
|
|
307
|
+
init_paths();
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
// libs/shield-daemon/src/command-sync.ts
|
|
312
|
+
var command_sync_exports = {};
|
|
313
|
+
__export(command_sync_exports, {
|
|
314
|
+
ensureWrappersInstalled: () => ensureWrappersInstalled,
|
|
315
|
+
syncCommandPolicies: () => syncCommandPolicies,
|
|
316
|
+
syncCommandPoliciesAndWrappers: () => syncCommandPoliciesAndWrappers
|
|
317
|
+
});
|
|
318
|
+
import * as fs5 from "node:fs";
|
|
319
|
+
import * as path5 from "node:path";
|
|
320
|
+
import { execSync as execSync3 } from "node:child_process";
|
|
321
|
+
import { PROXIED_COMMANDS, BASIC_SYSTEM_COMMANDS } from "@agenshield/sandbox";
|
|
322
|
+
function resolveCommandPaths(name) {
|
|
323
|
+
const paths = [];
|
|
324
|
+
for (const dir of BIN_SEARCH_DIRS) {
|
|
325
|
+
const candidate = path5.join(dir, name);
|
|
326
|
+
try {
|
|
327
|
+
if (fs5.existsSync(candidate)) {
|
|
328
|
+
const stat = fs5.statSync(candidate);
|
|
329
|
+
if (stat.isFile() && (stat.mode & 73) !== 0) {
|
|
330
|
+
paths.push(candidate);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
} catch {
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (paths.length === 0) {
|
|
337
|
+
try {
|
|
338
|
+
const result = execSync3(`which ${name} 2>/dev/null`, { encoding: "utf-8" }).trim();
|
|
339
|
+
if (result && path5.isAbsolute(result)) {
|
|
340
|
+
paths.push(result);
|
|
341
|
+
}
|
|
342
|
+
} catch {
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return paths;
|
|
346
|
+
}
|
|
347
|
+
function extractCommandName(pattern) {
|
|
348
|
+
return pattern.trim().split(/\s+/)[0];
|
|
349
|
+
}
|
|
350
|
+
function extractPolicyCommandNames(policies) {
|
|
351
|
+
const names = /* @__PURE__ */ new Set();
|
|
352
|
+
for (const p of policies) {
|
|
353
|
+
if (p.target === "command" && p.action === "allow" && p.enabled) {
|
|
354
|
+
for (const pattern of p.patterns) {
|
|
355
|
+
const name = extractCommandName(pattern);
|
|
356
|
+
if (name) names.add(name);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return names;
|
|
361
|
+
}
|
|
362
|
+
function syncCommandPolicies(policies, logger) {
|
|
363
|
+
const log = logger ?? noop2;
|
|
364
|
+
const commandPolicies = policies.filter(
|
|
365
|
+
(p) => p.target === "command" && p.action === "allow" && p.enabled
|
|
366
|
+
);
|
|
367
|
+
const commandNames = /* @__PURE__ */ new Set();
|
|
368
|
+
for (const policy of commandPolicies) {
|
|
369
|
+
for (const pattern of policy.patterns) {
|
|
370
|
+
const name = extractCommandName(pattern);
|
|
371
|
+
if (name) commandNames.add(name);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
const commands = [];
|
|
375
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
376
|
+
for (const name of commandNames) {
|
|
377
|
+
const paths = resolveCommandPaths(name);
|
|
378
|
+
if (paths.length === 0) {
|
|
379
|
+
log.warn(`[command-sync] command '${name}' not found on system, adding without paths`);
|
|
380
|
+
}
|
|
381
|
+
commands.push({
|
|
382
|
+
name,
|
|
383
|
+
paths,
|
|
384
|
+
addedAt: now,
|
|
385
|
+
addedBy: "policy",
|
|
386
|
+
category: "policy-managed"
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
const config = {
|
|
390
|
+
version: "1.0.0",
|
|
391
|
+
commands
|
|
392
|
+
};
|
|
393
|
+
const json = JSON.stringify(config, null, 2) + "\n";
|
|
394
|
+
try {
|
|
395
|
+
const dir = path5.dirname(ALLOWED_COMMANDS_PATH);
|
|
396
|
+
if (!fs5.existsSync(dir)) {
|
|
397
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
398
|
+
}
|
|
399
|
+
fs5.writeFileSync(ALLOWED_COMMANDS_PATH, json, "utf-8");
|
|
400
|
+
log.info(`[command-sync] wrote ${commands.length} commands to allowlist`);
|
|
401
|
+
} catch {
|
|
402
|
+
log.warn(`[command-sync] cannot write to ${ALLOWED_COMMANDS_PATH} (broker forwards to daemon for policy checks)`);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
function generateFallbackWrapper(cmd) {
|
|
406
|
+
return [
|
|
407
|
+
"#!/bin/bash",
|
|
408
|
+
`# ${cmd} - AgenShield proxy (auto-generated)`,
|
|
409
|
+
"if ! /bin/pwd > /dev/null 2>&1; then cd ~ 2>/dev/null || cd /; fi",
|
|
410
|
+
`exec /opt/agenshield/bin/shield-client exec ${cmd} "$@"`,
|
|
411
|
+
""
|
|
412
|
+
].join("\n");
|
|
413
|
+
}
|
|
414
|
+
function installWrappersInDir(binDir, log, policyCommands) {
|
|
415
|
+
const shieldExecPath = "/opt/agenshield/bin/shield-exec";
|
|
416
|
+
if (!fs5.existsSync(binDir)) {
|
|
417
|
+
try {
|
|
418
|
+
fs5.mkdirSync(binDir, { recursive: true, mode: 493 });
|
|
419
|
+
} catch {
|
|
420
|
+
log.warn(`[command-sync] cannot create bin dir ${binDir}`);
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
const hasShieldExec = fs5.existsSync(shieldExecPath);
|
|
425
|
+
if (!hasShieldExec) {
|
|
426
|
+
log.warn("[command-sync] shield-exec not found, using bash wrapper fallback");
|
|
427
|
+
}
|
|
428
|
+
for (const cmd of ALL_PROXIED_COMMANDS) {
|
|
429
|
+
const wrapperPath = path5.join(binDir, cmd);
|
|
430
|
+
if (fs5.existsSync(wrapperPath)) {
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
if (hasShieldExec) {
|
|
434
|
+
try {
|
|
435
|
+
fs5.symlinkSync(shieldExecPath, wrapperPath);
|
|
436
|
+
continue;
|
|
437
|
+
} catch {
|
|
438
|
+
log.warn(`[command-sync] cannot symlink ${cmd}, falling back to bash wrapper`);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
try {
|
|
442
|
+
fs5.writeFileSync(wrapperPath, generateFallbackWrapper(cmd), { mode: 493 });
|
|
443
|
+
} catch {
|
|
444
|
+
log.warn(`[command-sync] cannot write wrapper for ${cmd}`);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
if (policyCommands) {
|
|
448
|
+
for (const cmd of policyCommands) {
|
|
449
|
+
if (PROXIED_COMMANDS_SET.has(cmd)) continue;
|
|
450
|
+
if (BASIC_SYSTEM_COMMANDS_SET.has(cmd)) continue;
|
|
451
|
+
if (SPECIALIZED_WRAPPER_COMMANDS.has(cmd)) continue;
|
|
452
|
+
const wrapperPath = path5.join(binDir, cmd);
|
|
453
|
+
if (fs5.existsSync(wrapperPath)) continue;
|
|
454
|
+
if (hasShieldExec) {
|
|
455
|
+
try {
|
|
456
|
+
fs5.symlinkSync(shieldExecPath, wrapperPath);
|
|
457
|
+
log.info(`[command-sync] installed dynamic wrapper (symlink): ${cmd}`);
|
|
458
|
+
continue;
|
|
459
|
+
} catch {
|
|
460
|
+
log.warn(`[command-sync] cannot symlink ${cmd}, falling back to bash wrapper`);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
try {
|
|
464
|
+
fs5.writeFileSync(wrapperPath, generateFallbackWrapper(cmd), { mode: 493 });
|
|
465
|
+
log.info(`[command-sync] installed dynamic wrapper (bash): ${cmd}`);
|
|
466
|
+
} catch {
|
|
467
|
+
log.warn(`[command-sync] cannot write dynamic wrapper for ${cmd}`);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
function cleanupStaleWrappers(binDir, policyCommands, log) {
|
|
473
|
+
const shieldExecPath = "/opt/agenshield/bin/shield-exec";
|
|
474
|
+
let entries;
|
|
475
|
+
try {
|
|
476
|
+
entries = fs5.readdirSync(binDir);
|
|
477
|
+
} catch {
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
for (const entry of entries) {
|
|
481
|
+
if (PROXIED_COMMANDS_SET.has(entry)) continue;
|
|
482
|
+
if (SPECIALIZED_WRAPPER_COMMANDS.has(entry)) continue;
|
|
483
|
+
if (BASIC_SYSTEM_COMMANDS_SET.has(entry)) continue;
|
|
484
|
+
if (policyCommands.has(entry)) continue;
|
|
485
|
+
const wrapperPath = path5.join(binDir, entry);
|
|
486
|
+
try {
|
|
487
|
+
const stat = fs5.lstatSync(wrapperPath);
|
|
488
|
+
if (stat.isSymbolicLink()) {
|
|
489
|
+
const target = fs5.readlinkSync(wrapperPath);
|
|
490
|
+
if (target === shieldExecPath) {
|
|
491
|
+
fs5.unlinkSync(wrapperPath);
|
|
492
|
+
log.info(`[command-sync] removed stale dynamic wrapper (symlink): ${entry}`);
|
|
493
|
+
}
|
|
494
|
+
} else if (stat.isFile()) {
|
|
495
|
+
const content = fs5.readFileSync(wrapperPath, "utf-8");
|
|
496
|
+
if (content.includes("shield-client exec") && content.includes("AgenShield proxy (auto-generated)")) {
|
|
497
|
+
fs5.unlinkSync(wrapperPath);
|
|
498
|
+
log.info(`[command-sync] removed stale dynamic wrapper (bash): ${entry}`);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
} catch {
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
function ensureWrappersInstalled(state, logger, policyCommands) {
|
|
506
|
+
const log = logger ?? noop2;
|
|
507
|
+
const agentUser = state.users.find((u) => u.type === "agent");
|
|
508
|
+
if (!agentUser) {
|
|
509
|
+
log.warn("[command-sync] no agent user in state, skipping wrapper installation");
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
const agentBinDir = path5.join(agentUser.homeDir, "bin");
|
|
513
|
+
log.info(`[command-sync] ensuring wrappers in agent bin: ${agentBinDir}`);
|
|
514
|
+
installWrappersInDir(agentBinDir, log, policyCommands);
|
|
515
|
+
if (policyCommands) {
|
|
516
|
+
cleanupStaleWrappers(agentBinDir, policyCommands, log);
|
|
517
|
+
}
|
|
518
|
+
const agentHomeEnv = process.env["AGENSHIELD_AGENT_HOME"];
|
|
519
|
+
if (agentHomeEnv && agentHomeEnv !== agentUser.homeDir) {
|
|
520
|
+
const envBinDir = path5.join(agentHomeEnv, "bin");
|
|
521
|
+
log.info(`[command-sync] ensuring wrappers in env agent bin: ${envBinDir}`);
|
|
522
|
+
installWrappersInDir(envBinDir, log, policyCommands);
|
|
523
|
+
if (policyCommands) {
|
|
524
|
+
cleanupStaleWrappers(envBinDir, policyCommands, log);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
function syncCommandPoliciesAndWrappers(policies, state, logger) {
|
|
529
|
+
const policyCommands = extractPolicyCommandNames(policies);
|
|
530
|
+
syncCommandPolicies(policies, logger);
|
|
531
|
+
ensureWrappersInstalled(state, logger, policyCommands);
|
|
532
|
+
}
|
|
533
|
+
var noop2, ALLOWED_COMMANDS_PATH, BIN_SEARCH_DIRS, ALL_PROXIED_COMMANDS, BASIC_SYSTEM_COMMANDS_SET, SPECIALIZED_WRAPPER_COMMANDS, PROXIED_COMMANDS_SET;
|
|
534
|
+
var init_command_sync = __esm({
|
|
535
|
+
"libs/shield-daemon/src/command-sync.ts"() {
|
|
536
|
+
"use strict";
|
|
537
|
+
noop2 = { warn() {
|
|
538
|
+
}, info() {
|
|
539
|
+
} };
|
|
540
|
+
ALLOWED_COMMANDS_PATH = "/opt/agenshield/config/allowed-commands.json";
|
|
541
|
+
BIN_SEARCH_DIRS = [
|
|
542
|
+
"/usr/bin",
|
|
543
|
+
"/usr/local/bin",
|
|
544
|
+
"/opt/homebrew/bin",
|
|
545
|
+
"/usr/sbin",
|
|
546
|
+
"/usr/local/sbin",
|
|
547
|
+
"/opt/homebrew/sbin"
|
|
548
|
+
];
|
|
549
|
+
ALL_PROXIED_COMMANDS = [...PROXIED_COMMANDS];
|
|
550
|
+
BASIC_SYSTEM_COMMANDS_SET = new Set(BASIC_SYSTEM_COMMANDS);
|
|
551
|
+
SPECIALIZED_WRAPPER_COMMANDS = /* @__PURE__ */ new Set(["node", "python", "python3"]);
|
|
552
|
+
PROXIED_COMMANDS_SET = new Set(ALL_PROXIED_COMMANDS);
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
// libs/shield-daemon/src/main.ts
|
|
557
|
+
import * as fs20 from "node:fs";
|
|
558
|
+
|
|
559
|
+
// libs/shield-daemon/src/config/index.ts
|
|
560
|
+
init_paths();
|
|
561
|
+
init_defaults();
|
|
562
|
+
init_loader();
|
|
563
|
+
|
|
564
|
+
// libs/shield-daemon/src/server.ts
|
|
565
|
+
import * as fs19 from "node:fs";
|
|
566
|
+
import Fastify from "fastify";
|
|
567
|
+
import cors from "@fastify/cors";
|
|
568
|
+
import fastifyStatic from "@fastify/static";
|
|
569
|
+
|
|
570
|
+
// libs/shield-daemon/src/routes/index.ts
|
|
571
|
+
import { API_PREFIX } from "@agenshield/ipc";
|
|
572
|
+
|
|
573
|
+
// libs/shield-daemon/src/routes/health.ts
|
|
574
|
+
async function healthRoutes(app) {
|
|
575
|
+
app.get("/health", async () => {
|
|
576
|
+
return {
|
|
577
|
+
success: true,
|
|
578
|
+
data: {
|
|
579
|
+
ok: true,
|
|
580
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
581
|
+
mode: "daemon"
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
});
|
|
585
|
+
}
|
|
188
586
|
|
|
189
587
|
// libs/shield-daemon/src/routes/status.ts
|
|
588
|
+
init_state();
|
|
190
589
|
var startedAt = /* @__PURE__ */ new Date();
|
|
191
590
|
async function statusRoutes(app) {
|
|
192
591
|
app.get("/status", async () => {
|
|
@@ -214,7 +613,8 @@ async function statusRoutes(app) {
|
|
|
214
613
|
|
|
215
614
|
// libs/shield-daemon/src/routes/config.ts
|
|
216
615
|
import * as fs6 from "node:fs";
|
|
217
|
-
import * as
|
|
616
|
+
import * as path6 from "node:path";
|
|
617
|
+
init_state();
|
|
218
618
|
|
|
219
619
|
// libs/shield-daemon/src/vault/index.ts
|
|
220
620
|
import * as fs3 from "node:fs";
|
|
@@ -275,6 +675,7 @@ function decrypt(encryptedData, key) {
|
|
|
275
675
|
}
|
|
276
676
|
|
|
277
677
|
// libs/shield-daemon/src/vault/index.ts
|
|
678
|
+
init_paths();
|
|
278
679
|
var Vault = class {
|
|
279
680
|
key;
|
|
280
681
|
vaultPath;
|
|
@@ -518,8 +919,30 @@ function getSessionManager() {
|
|
|
518
919
|
import { execSync as execSync2 } from "node:child_process";
|
|
519
920
|
import * as os3 from "node:os";
|
|
520
921
|
import * as fs4 from "node:fs";
|
|
922
|
+
import * as path4 from "node:path";
|
|
521
923
|
var noop = { warn() {
|
|
522
924
|
} };
|
|
925
|
+
var TRAVERSAL_PERMS = "search";
|
|
926
|
+
var WORLD_TRAVERSABLE_PATHS = /* @__PURE__ */ new Set([
|
|
927
|
+
"/",
|
|
928
|
+
"/Users",
|
|
929
|
+
"/tmp",
|
|
930
|
+
"/private",
|
|
931
|
+
"/private/tmp",
|
|
932
|
+
"/private/var",
|
|
933
|
+
"/var",
|
|
934
|
+
"/opt",
|
|
935
|
+
"/usr",
|
|
936
|
+
"/usr/local",
|
|
937
|
+
"/Applications",
|
|
938
|
+
"/Library",
|
|
939
|
+
"/System",
|
|
940
|
+
"/Volumes"
|
|
941
|
+
]);
|
|
942
|
+
function normalizePath(p) {
|
|
943
|
+
if (p === "/") return p;
|
|
944
|
+
return p.replace(/\/+$/, "") || "/";
|
|
945
|
+
}
|
|
523
946
|
function stripGlobToBasePath(pattern) {
|
|
524
947
|
let p = pattern;
|
|
525
948
|
if (p.startsWith("~")) {
|
|
@@ -531,7 +954,7 @@ function stripGlobToBasePath(pattern) {
|
|
|
531
954
|
if (/[*?[]/.test(seg)) break;
|
|
532
955
|
base.push(seg);
|
|
533
956
|
}
|
|
534
|
-
return base.length === 0 ? "/" : base.join("/") || "/";
|
|
957
|
+
return normalizePath(base.length === 0 ? "/" : base.join("/") || "/");
|
|
535
958
|
}
|
|
536
959
|
function operationsToAclPerms(operations) {
|
|
537
960
|
const perms = [];
|
|
@@ -589,197 +1012,69 @@ function removeGroupAcl(targetPath, groupName, log = noop) {
|
|
|
589
1012
|
log.warn(`[acl] failed to read ACLs on ${targetPath}: ${err.message}`);
|
|
590
1013
|
}
|
|
591
1014
|
}
|
|
592
|
-
function
|
|
593
|
-
const
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
for (const [id, oldP] of oldMap) {
|
|
599
|
-
if (!newMap.has(id)) {
|
|
600
|
-
for (const pattern of oldP.patterns) {
|
|
601
|
-
removeGroupAcl(stripGlobToBasePath(pattern), groupName, log);
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
for (const [id, newP] of newMap) {
|
|
606
|
-
if (!oldMap.has(id)) {
|
|
607
|
-
const perms = operationsToAclPerms(newP.operations ?? []);
|
|
608
|
-
if (!perms) continue;
|
|
609
|
-
for (const pattern of newP.patterns) {
|
|
610
|
-
addGroupAcl(stripGlobToBasePath(pattern), groupName, perms, log);
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
for (const [id, newP] of newMap) {
|
|
615
|
-
const oldP = oldMap.get(id);
|
|
616
|
-
if (!oldP) continue;
|
|
617
|
-
const patternsChanged = JSON.stringify(oldP.patterns) !== JSON.stringify(newP.patterns);
|
|
618
|
-
const opsChanged = JSON.stringify(oldP.operations ?? []) !== JSON.stringify(newP.operations ?? []);
|
|
619
|
-
if (patternsChanged || opsChanged) {
|
|
620
|
-
for (const pattern of oldP.patterns) {
|
|
621
|
-
removeGroupAcl(stripGlobToBasePath(pattern), groupName, log);
|
|
622
|
-
}
|
|
623
|
-
const perms = operationsToAclPerms(newP.operations ?? []);
|
|
624
|
-
if (!perms) continue;
|
|
625
|
-
for (const pattern of newP.patterns) {
|
|
626
|
-
addGroupAcl(stripGlobToBasePath(pattern), groupName, perms, log);
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
// libs/shield-daemon/src/command-sync.ts
|
|
633
|
-
import * as fs5 from "node:fs";
|
|
634
|
-
import * as path4 from "node:path";
|
|
635
|
-
import { execSync as execSync3 } from "node:child_process";
|
|
636
|
-
var noop2 = { warn() {
|
|
637
|
-
}, info() {
|
|
638
|
-
} };
|
|
639
|
-
var ALLOWED_COMMANDS_PATH = "/opt/agenshield/config/allowed-commands.json";
|
|
640
|
-
var BIN_SEARCH_DIRS = [
|
|
641
|
-
"/usr/bin",
|
|
642
|
-
"/usr/local/bin",
|
|
643
|
-
"/opt/homebrew/bin",
|
|
644
|
-
"/usr/sbin",
|
|
645
|
-
"/usr/local/sbin",
|
|
646
|
-
"/opt/homebrew/sbin"
|
|
647
|
-
];
|
|
648
|
-
var ALL_PROXIED_COMMANDS = [
|
|
649
|
-
"curl",
|
|
650
|
-
"wget",
|
|
651
|
-
"git",
|
|
652
|
-
"ssh",
|
|
653
|
-
"scp",
|
|
654
|
-
"rsync",
|
|
655
|
-
"brew",
|
|
656
|
-
"npm",
|
|
657
|
-
"npx",
|
|
658
|
-
"pip",
|
|
659
|
-
"pip3",
|
|
660
|
-
"open-url",
|
|
661
|
-
"shieldctl",
|
|
662
|
-
"agenco"
|
|
663
|
-
];
|
|
664
|
-
function resolveCommandPaths(name) {
|
|
665
|
-
const paths = [];
|
|
666
|
-
for (const dir of BIN_SEARCH_DIRS) {
|
|
667
|
-
const candidate = path4.join(dir, name);
|
|
668
|
-
try {
|
|
669
|
-
if (fs5.existsSync(candidate)) {
|
|
670
|
-
const stat = fs5.statSync(candidate);
|
|
671
|
-
if (stat.isFile() && (stat.mode & 73) !== 0) {
|
|
672
|
-
paths.push(candidate);
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
} catch {
|
|
1015
|
+
function getAncestorsNeedingTraversal(targetPath) {
|
|
1016
|
+
const ancestors = [];
|
|
1017
|
+
let dir = path4.dirname(targetPath);
|
|
1018
|
+
while (dir !== targetPath && dir !== "/") {
|
|
1019
|
+
if (!WORLD_TRAVERSABLE_PATHS.has(dir)) {
|
|
1020
|
+
ancestors.push(dir);
|
|
676
1021
|
}
|
|
1022
|
+
targetPath = dir;
|
|
1023
|
+
dir = path4.dirname(dir);
|
|
677
1024
|
}
|
|
678
|
-
|
|
679
|
-
try {
|
|
680
|
-
const result = execSync3(`which ${name} 2>/dev/null`, { encoding: "utf-8" }).trim();
|
|
681
|
-
if (result && path4.isAbsolute(result)) {
|
|
682
|
-
paths.push(result);
|
|
683
|
-
}
|
|
684
|
-
} catch {
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
return paths;
|
|
1025
|
+
return ancestors;
|
|
688
1026
|
}
|
|
689
|
-
function
|
|
690
|
-
|
|
1027
|
+
function mergePerms(a, b) {
|
|
1028
|
+
const set = /* @__PURE__ */ new Set([
|
|
1029
|
+
...a.split(",").filter(Boolean),
|
|
1030
|
+
...b.split(",").filter(Boolean)
|
|
1031
|
+
]);
|
|
1032
|
+
return [...set].join(",");
|
|
691
1033
|
}
|
|
692
|
-
function
|
|
693
|
-
const
|
|
694
|
-
const
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
const commandNames = /* @__PURE__ */ new Set();
|
|
698
|
-
for (const policy of commandPolicies) {
|
|
1034
|
+
function computeAclMap(policies) {
|
|
1035
|
+
const aclMap = /* @__PURE__ */ new Map();
|
|
1036
|
+
for (const policy of policies.filter((p) => p.action === "allow")) {
|
|
1037
|
+
const perms = operationsToAclPerms(policy.operations ?? []);
|
|
1038
|
+
if (!perms) continue;
|
|
699
1039
|
for (const pattern of policy.patterns) {
|
|
700
|
-
const
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
}
|
|
704
|
-
const commands = [];
|
|
705
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
706
|
-
for (const name of commandNames) {
|
|
707
|
-
const paths = resolveCommandPaths(name);
|
|
708
|
-
if (paths.length === 0) {
|
|
709
|
-
log.warn(`[command-sync] command '${name}' not found on system, adding without paths`);
|
|
710
|
-
}
|
|
711
|
-
commands.push({
|
|
712
|
-
name,
|
|
713
|
-
paths,
|
|
714
|
-
addedAt: now,
|
|
715
|
-
addedBy: "policy",
|
|
716
|
-
category: "policy-managed"
|
|
717
|
-
});
|
|
718
|
-
}
|
|
719
|
-
const config = {
|
|
720
|
-
version: "1.0.0",
|
|
721
|
-
commands
|
|
722
|
-
};
|
|
723
|
-
try {
|
|
724
|
-
const dir = path4.dirname(ALLOWED_COMMANDS_PATH);
|
|
725
|
-
if (!fs5.existsSync(dir)) {
|
|
726
|
-
fs5.mkdirSync(dir, { recursive: true });
|
|
727
|
-
}
|
|
728
|
-
fs5.writeFileSync(ALLOWED_COMMANDS_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
729
|
-
log.info(`[command-sync] wrote ${commands.length} commands to allowlist`);
|
|
730
|
-
} catch (err) {
|
|
731
|
-
log.warn(`[command-sync] failed to write allowlist: ${err.message}`);
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
function installWrappersInDir(binDir, log) {
|
|
735
|
-
const shieldExecPath = "/opt/agenshield/bin/shield-exec";
|
|
736
|
-
if (!fs5.existsSync(binDir)) {
|
|
737
|
-
try {
|
|
738
|
-
fs5.mkdirSync(binDir, { recursive: true, mode: 493 });
|
|
739
|
-
} catch (err) {
|
|
740
|
-
log.warn(`[command-sync] failed to create bin dir ${binDir}: ${err.message}`);
|
|
741
|
-
return;
|
|
1040
|
+
const target = stripGlobToBasePath(pattern);
|
|
1041
|
+
const existing = aclMap.get(target);
|
|
1042
|
+
aclMap.set(target, existing ? mergePerms(existing, perms) : perms);
|
|
742
1043
|
}
|
|
743
1044
|
}
|
|
744
|
-
for (const
|
|
745
|
-
const
|
|
746
|
-
if (
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
log.warn(`[command-sync] shield-exec not found at ${shieldExecPath}, skipping ${cmd} wrapper`);
|
|
1045
|
+
for (const policy of policies.filter((p) => p.action === "allow")) {
|
|
1046
|
+
const perms = operationsToAclPerms(policy.operations ?? []);
|
|
1047
|
+
if (!perms) continue;
|
|
1048
|
+
for (const pattern of policy.patterns) {
|
|
1049
|
+
const target = stripGlobToBasePath(pattern);
|
|
1050
|
+
for (const ancestor of getAncestorsNeedingTraversal(target)) {
|
|
1051
|
+
if (!aclMap.has(ancestor)) {
|
|
1052
|
+
aclMap.set(ancestor, TRAVERSAL_PERMS);
|
|
1053
|
+
}
|
|
754
1054
|
}
|
|
755
|
-
} catch (err) {
|
|
756
|
-
log.warn(`[command-sync] failed to create wrapper ${wrapperPath}: ${err.message}`);
|
|
757
1055
|
}
|
|
758
1056
|
}
|
|
1057
|
+
return aclMap;
|
|
759
1058
|
}
|
|
760
|
-
function
|
|
761
|
-
const log = logger ??
|
|
762
|
-
const
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
1059
|
+
function syncFilesystemPolicyAcls(oldPolicies, newPolicies, groupName, logger) {
|
|
1060
|
+
const log = logger ?? noop;
|
|
1061
|
+
const oldFs = oldPolicies.filter((p) => p.target === "filesystem");
|
|
1062
|
+
const newFs = newPolicies.filter((p) => p.target === "filesystem");
|
|
1063
|
+
const oldAclMap = computeAclMap(oldFs);
|
|
1064
|
+
const newAclMap = computeAclMap(newFs);
|
|
1065
|
+
for (const oldPath of oldAclMap.keys()) {
|
|
1066
|
+
if (!newAclMap.has(oldPath)) {
|
|
1067
|
+
removeGroupAcl(oldPath, groupName, log);
|
|
1068
|
+
}
|
|
766
1069
|
}
|
|
767
|
-
const
|
|
768
|
-
|
|
769
|
-
installWrappersInDir(agentBinDir, log);
|
|
770
|
-
const agentHomeEnv = process.env["AGENSHIELD_AGENT_HOME"];
|
|
771
|
-
if (agentHomeEnv && agentHomeEnv !== agentUser.homeDir) {
|
|
772
|
-
const envBinDir = path4.join(agentHomeEnv, "bin");
|
|
773
|
-
log.info(`[command-sync] ensuring wrappers in env agent bin: ${envBinDir}`);
|
|
774
|
-
installWrappersInDir(envBinDir, log);
|
|
1070
|
+
for (const [targetPath, perms] of newAclMap) {
|
|
1071
|
+
addGroupAcl(targetPath, groupName, perms, log);
|
|
775
1072
|
}
|
|
776
1073
|
}
|
|
777
|
-
function syncCommandPoliciesAndWrappers(policies, state, logger) {
|
|
778
|
-
syncCommandPolicies(policies, logger);
|
|
779
|
-
ensureWrappersInstalled(state, logger);
|
|
780
|
-
}
|
|
781
1074
|
|
|
782
1075
|
// libs/shield-daemon/src/routes/config.ts
|
|
1076
|
+
init_command_sync();
|
|
1077
|
+
import { installShieldExec, createUserConfig } from "@agenshield/sandbox";
|
|
783
1078
|
async function configRoutes(app) {
|
|
784
1079
|
app.get("/config", async () => {
|
|
785
1080
|
const config = loadConfig();
|
|
@@ -841,9 +1136,31 @@ async function configRoutes(app) {
|
|
|
841
1136
|
};
|
|
842
1137
|
}
|
|
843
1138
|
});
|
|
1139
|
+
app.post("/config/install-wrappers", async () => {
|
|
1140
|
+
try {
|
|
1141
|
+
const state = loadState();
|
|
1142
|
+
const agentUser = state.users.find((u) => u.type === "agent");
|
|
1143
|
+
if (!agentUser) {
|
|
1144
|
+
return { success: false, error: "No agent user found in state" };
|
|
1145
|
+
}
|
|
1146
|
+
const userConfig = createUserConfig();
|
|
1147
|
+
const binDir = path6.join(agentUser.homeDir, "bin");
|
|
1148
|
+
const result = await installShieldExec(userConfig, binDir);
|
|
1149
|
+
return {
|
|
1150
|
+
success: result.success,
|
|
1151
|
+
installed: result.installed,
|
|
1152
|
+
error: result.error
|
|
1153
|
+
};
|
|
1154
|
+
} catch (error) {
|
|
1155
|
+
return {
|
|
1156
|
+
success: false,
|
|
1157
|
+
error: error instanceof Error ? error.message : "Failed to install wrappers"
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
});
|
|
844
1161
|
app.get("/config/openclaw", async (_request, reply) => {
|
|
845
1162
|
const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
|
|
846
|
-
const configDir =
|
|
1163
|
+
const configDir = path6.join(agentHome, ".openclaw");
|
|
847
1164
|
const configFiles = readConfigDir(configDir);
|
|
848
1165
|
return reply.send({ configDir, files: configFiles });
|
|
849
1166
|
});
|
|
@@ -855,7 +1172,7 @@ async function configRoutes(app) {
|
|
|
855
1172
|
return reply.code(400).send({ error: "original query param required" });
|
|
856
1173
|
}
|
|
857
1174
|
const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
|
|
858
|
-
const agentConfigDir =
|
|
1175
|
+
const agentConfigDir = path6.join(agentHome, ".openclaw");
|
|
859
1176
|
const diff = diffConfigDirs(original, agentConfigDir);
|
|
860
1177
|
return reply.send({ diff });
|
|
861
1178
|
}
|
|
@@ -877,13 +1194,13 @@ function readConfigDir(dir, base) {
|
|
|
877
1194
|
for (const entry of entries) {
|
|
878
1195
|
if (entry.isDirectory()) {
|
|
879
1196
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
880
|
-
const sub = readConfigDir(
|
|
1197
|
+
const sub = readConfigDir(path6.join(dir, entry.name), root);
|
|
881
1198
|
Object.assign(result, sub);
|
|
882
1199
|
} else if (entry.isFile()) {
|
|
883
|
-
const ext =
|
|
1200
|
+
const ext = path6.extname(entry.name).toLowerCase();
|
|
884
1201
|
if ([".json", ".yaml", ".yml", ".toml", ".txt", ".md", ".conf", ""].includes(ext)) {
|
|
885
|
-
const filePath =
|
|
886
|
-
const relPath =
|
|
1202
|
+
const filePath = path6.join(dir, entry.name);
|
|
1203
|
+
const relPath = path6.relative(root, filePath);
|
|
887
1204
|
try {
|
|
888
1205
|
result[relPath] = fs6.readFileSync(filePath, "utf-8");
|
|
889
1206
|
} catch {
|
|
@@ -1005,10 +1322,10 @@ function emitSecurityWarning(warning) {
|
|
|
1005
1322
|
function emitSecurityCritical(issue) {
|
|
1006
1323
|
daemonEvents.broadcast("security:critical", { message: issue });
|
|
1007
1324
|
}
|
|
1008
|
-
function emitApiRequest(method,
|
|
1325
|
+
function emitApiRequest(method, path19, statusCode, duration, requestBody, responseBody) {
|
|
1009
1326
|
daemonEvents.broadcast("api:request", {
|
|
1010
1327
|
method,
|
|
1011
|
-
path:
|
|
1328
|
+
path: path19,
|
|
1012
1329
|
statusCode,
|
|
1013
1330
|
duration,
|
|
1014
1331
|
...requestBody !== void 0 && { requestBody },
|
|
@@ -1051,6 +1368,7 @@ function emitEvent(type, data) {
|
|
|
1051
1368
|
|
|
1052
1369
|
// libs/shield-daemon/src/auth/passcode.ts
|
|
1053
1370
|
import * as crypto3 from "node:crypto";
|
|
1371
|
+
init_state();
|
|
1054
1372
|
import { DEFAULT_AUTH_CONFIG as DEFAULT_AUTH_CONFIG2 } from "@agenshield/ipc";
|
|
1055
1373
|
var ITERATIONS = 1e5;
|
|
1056
1374
|
var KEY_LENGTH = 64;
|
|
@@ -1215,16 +1533,16 @@ var PROTECTED_ROUTES = [
|
|
|
1215
1533
|
{ method: "POST", path: "/api/config/factory-reset" },
|
|
1216
1534
|
{ method: "POST", path: "/api/skills/install" }
|
|
1217
1535
|
];
|
|
1218
|
-
function isProtectedRoute(method,
|
|
1536
|
+
function isProtectedRoute(method, path19) {
|
|
1219
1537
|
return PROTECTED_ROUTES.some(
|
|
1220
|
-
(route) => route.method === method &&
|
|
1538
|
+
(route) => route.method === method && path19.startsWith(route.path)
|
|
1221
1539
|
);
|
|
1222
1540
|
}
|
|
1223
1541
|
function createAuthHook() {
|
|
1224
1542
|
return async (request, reply) => {
|
|
1225
1543
|
const method = request.method;
|
|
1226
|
-
const
|
|
1227
|
-
if (!isProtectedRoute(method,
|
|
1544
|
+
const path19 = request.url.split("?")[0];
|
|
1545
|
+
if (!isProtectedRoute(method, path19)) {
|
|
1228
1546
|
return;
|
|
1229
1547
|
}
|
|
1230
1548
|
if (!isAuthenticated(request)) {
|
|
@@ -1731,6 +2049,9 @@ async function loggedFetch(url, init, context) {
|
|
|
1731
2049
|
}
|
|
1732
2050
|
}
|
|
1733
2051
|
|
|
2052
|
+
// libs/shield-daemon/src/routes/agenco.ts
|
|
2053
|
+
init_state();
|
|
2054
|
+
|
|
1734
2055
|
// libs/shield-daemon/src/mcp/client.ts
|
|
1735
2056
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
1736
2057
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
@@ -2057,7 +2378,7 @@ function getMCPState() {
|
|
|
2057
2378
|
|
|
2058
2379
|
// libs/shield-daemon/src/services/integration-skills.ts
|
|
2059
2380
|
import * as fs8 from "node:fs";
|
|
2060
|
-
import * as
|
|
2381
|
+
import * as path8 from "node:path";
|
|
2061
2382
|
|
|
2062
2383
|
// libs/shield-skills/dist/index.js
|
|
2063
2384
|
import * as path22 from "node:path";
|
|
@@ -2123,7 +2444,7 @@ var BUILTIN_SKILLS_DIR = path22.resolve(__skills_dirname, "..", "skills");
|
|
|
2123
2444
|
|
|
2124
2445
|
// libs/shield-daemon/src/watchers/skills.ts
|
|
2125
2446
|
import * as fs7 from "node:fs";
|
|
2126
|
-
import * as
|
|
2447
|
+
import * as path7 from "node:path";
|
|
2127
2448
|
import { execSync as execSync4 } from "node:child_process";
|
|
2128
2449
|
var APPROVED_SKILLS_PATH = "/opt/agenshield/config/approved-skills.json";
|
|
2129
2450
|
var QUARANTINE_DIR = "/opt/agenshield/quarantine/skills";
|
|
@@ -2145,7 +2466,7 @@ function loadApprovedSkills() {
|
|
|
2145
2466
|
}
|
|
2146
2467
|
function saveApprovedSkills(skills) {
|
|
2147
2468
|
try {
|
|
2148
|
-
const dir =
|
|
2469
|
+
const dir = path7.dirname(APPROVED_SKILLS_PATH);
|
|
2149
2470
|
fs7.mkdirSync(dir, { recursive: true });
|
|
2150
2471
|
const content = JSON.stringify(skills, null, 2);
|
|
2151
2472
|
fs7.writeFileSync(APPROVED_SKILLS_PATH, content, "utf-8");
|
|
@@ -2159,7 +2480,7 @@ function isApproved(skillName) {
|
|
|
2159
2480
|
}
|
|
2160
2481
|
function quarantineSkill(skillName, skillPath) {
|
|
2161
2482
|
try {
|
|
2162
|
-
const quarantinePath =
|
|
2483
|
+
const quarantinePath = path7.join(QUARANTINE_DIR, skillName);
|
|
2163
2484
|
if (!fs7.existsSync(QUARANTINE_DIR)) {
|
|
2164
2485
|
fs7.mkdirSync(QUARANTINE_DIR, { recursive: true, mode: 448 });
|
|
2165
2486
|
}
|
|
@@ -2192,7 +2513,7 @@ function scanSkills() {
|
|
|
2192
2513
|
if (!entry.isDirectory()) continue;
|
|
2193
2514
|
const skillName = entry.name;
|
|
2194
2515
|
if (!isApproved(skillName)) {
|
|
2195
|
-
const fullPath =
|
|
2516
|
+
const fullPath = path7.join(skillsDir, skillName);
|
|
2196
2517
|
const info = quarantineSkill(skillName, fullPath);
|
|
2197
2518
|
if (info && callbacks.onQuarantined) {
|
|
2198
2519
|
callbacks.onQuarantined(info);
|
|
@@ -2258,8 +2579,8 @@ function stopSkillsWatcher() {
|
|
|
2258
2579
|
}
|
|
2259
2580
|
function approveSkill(skillName) {
|
|
2260
2581
|
try {
|
|
2261
|
-
const quarantinedPath =
|
|
2262
|
-
const destPath =
|
|
2582
|
+
const quarantinedPath = path7.join(QUARANTINE_DIR, skillName);
|
|
2583
|
+
const destPath = path7.join(skillsDir, skillName);
|
|
2263
2584
|
if (!fs7.existsSync(quarantinedPath)) {
|
|
2264
2585
|
return { success: false, error: `Skill "${skillName}" not found in quarantine` };
|
|
2265
2586
|
}
|
|
@@ -2285,7 +2606,7 @@ function approveSkill(skillName) {
|
|
|
2285
2606
|
}
|
|
2286
2607
|
function rejectSkill(skillName) {
|
|
2287
2608
|
try {
|
|
2288
|
-
const quarantinedPath =
|
|
2609
|
+
const quarantinedPath = path7.join(QUARANTINE_DIR, skillName);
|
|
2289
2610
|
if (!fs7.existsSync(quarantinedPath)) {
|
|
2290
2611
|
return { success: false, error: `Skill "${skillName}" not found in quarantine` };
|
|
2291
2612
|
}
|
|
@@ -2301,7 +2622,7 @@ function revokeSkill(skillName) {
|
|
|
2301
2622
|
const approved = loadApprovedSkills();
|
|
2302
2623
|
const filtered = approved.filter((s) => s.name !== skillName);
|
|
2303
2624
|
saveApprovedSkills(filtered);
|
|
2304
|
-
const skillPath =
|
|
2625
|
+
const skillPath = path7.join(skillsDir, skillName);
|
|
2305
2626
|
if (fs7.existsSync(skillPath)) {
|
|
2306
2627
|
quarantineSkill(skillName, skillPath);
|
|
2307
2628
|
}
|
|
@@ -2320,11 +2641,11 @@ function listQuarantined() {
|
|
|
2320
2641
|
const entries = fs7.readdirSync(QUARANTINE_DIR, { withFileTypes: true });
|
|
2321
2642
|
for (const entry of entries) {
|
|
2322
2643
|
if (entry.isDirectory()) {
|
|
2323
|
-
const stat = fs7.statSync(
|
|
2644
|
+
const stat = fs7.statSync(path7.join(QUARANTINE_DIR, entry.name));
|
|
2324
2645
|
results.push({
|
|
2325
2646
|
name: entry.name,
|
|
2326
2647
|
quarantinedAt: stat.mtime.toISOString(),
|
|
2327
|
-
originalPath:
|
|
2648
|
+
originalPath: path7.join(skillsDir, entry.name),
|
|
2328
2649
|
reason: "Skill not in approved list"
|
|
2329
2650
|
});
|
|
2330
2651
|
}
|
|
@@ -2364,8 +2685,8 @@ function copyDirSync(src, dest) {
|
|
|
2364
2685
|
fs8.mkdirSync(dest, { recursive: true });
|
|
2365
2686
|
const entries = fs8.readdirSync(src, { withFileTypes: true });
|
|
2366
2687
|
for (const entry of entries) {
|
|
2367
|
-
const srcPath =
|
|
2368
|
-
const destPath =
|
|
2688
|
+
const srcPath = path8.join(src, entry.name);
|
|
2689
|
+
const destPath = path8.join(dest, entry.name);
|
|
2369
2690
|
if (entry.isDirectory()) {
|
|
2370
2691
|
copyDirSync(srcPath, destPath);
|
|
2371
2692
|
} else {
|
|
@@ -2379,8 +2700,8 @@ async function provisionAgenCoSkill() {
|
|
|
2379
2700
|
console.warn("[IntegrationSkills] Skills directory not configured \u2014 skipping provision");
|
|
2380
2701
|
return { installed: false };
|
|
2381
2702
|
}
|
|
2382
|
-
const destDir =
|
|
2383
|
-
const srcDir =
|
|
2703
|
+
const destDir = path8.join(skillsDir2, AGENCO_SKILL_NAME);
|
|
2704
|
+
const srcDir = path8.join(BUILTIN_SKILLS_DIR, AGENCO_SKILL_NAME);
|
|
2384
2705
|
if (fs8.existsSync(destDir)) {
|
|
2385
2706
|
return { installed: false };
|
|
2386
2707
|
}
|
|
@@ -2408,8 +2729,8 @@ async function provisionIntegrationSkill(integrationSlug) {
|
|
|
2408
2729
|
return { installed: false };
|
|
2409
2730
|
}
|
|
2410
2731
|
const skillName = `integration-${integrationSlug}`;
|
|
2411
|
-
const destDir =
|
|
2412
|
-
const srcDir =
|
|
2732
|
+
const destDir = path8.join(skillsDir2, skillName);
|
|
2733
|
+
const srcDir = path8.join(BUILTIN_SKILLS_DIR, skillName);
|
|
2413
2734
|
if (fs8.existsSync(destDir)) {
|
|
2414
2735
|
return { installed: false };
|
|
2415
2736
|
}
|
|
@@ -4949,13 +5270,13 @@ async function agencoRoutes(app) {
|
|
|
4949
5270
|
|
|
4950
5271
|
// libs/shield-daemon/src/routes/skills.ts
|
|
4951
5272
|
import * as fs13 from "node:fs";
|
|
4952
|
-
import * as
|
|
5273
|
+
import * as path13 from "node:path";
|
|
4953
5274
|
import { execSync as execSync8 } from "node:child_process";
|
|
4954
5275
|
import { parseSkillMd } from "@agenshield/sandbox";
|
|
4955
5276
|
|
|
4956
5277
|
// libs/shield-daemon/src/services/skill-analyzer.ts
|
|
4957
5278
|
import * as fs9 from "node:fs";
|
|
4958
|
-
import * as
|
|
5279
|
+
import * as path9 from "node:path";
|
|
4959
5280
|
import { execSync as execSync5 } from "node:child_process";
|
|
4960
5281
|
var ANALYSIS_CACHE_PATH = "/opt/agenshield/config/skill-analyses.json";
|
|
4961
5282
|
var COMMAND_PATTERNS = [
|
|
@@ -5026,7 +5347,7 @@ function loadCache() {
|
|
|
5026
5347
|
}
|
|
5027
5348
|
function saveCache(cache3) {
|
|
5028
5349
|
try {
|
|
5029
|
-
const dir =
|
|
5350
|
+
const dir = path9.dirname(ANALYSIS_CACHE_PATH);
|
|
5030
5351
|
if (!fs9.existsSync(dir)) {
|
|
5031
5352
|
fs9.mkdirSync(dir, { recursive: true });
|
|
5032
5353
|
}
|
|
@@ -5173,13 +5494,13 @@ function clearCachedAnalysis(skillName) {
|
|
|
5173
5494
|
|
|
5174
5495
|
// libs/shield-daemon/src/services/skill-lifecycle.ts
|
|
5175
5496
|
import * as fs10 from "node:fs";
|
|
5176
|
-
import * as
|
|
5497
|
+
import * as path10 from "node:path";
|
|
5177
5498
|
import { execSync as execSync6 } from "node:child_process";
|
|
5178
5499
|
function createSkillWrapper(name, binDir) {
|
|
5179
5500
|
if (!fs10.existsSync(binDir)) {
|
|
5180
5501
|
fs10.mkdirSync(binDir, { recursive: true });
|
|
5181
5502
|
}
|
|
5182
|
-
const wrapperPath =
|
|
5503
|
+
const wrapperPath = path10.join(binDir, name);
|
|
5183
5504
|
const wrapperContent = `#!/bin/bash
|
|
5184
5505
|
# ${name} skill wrapper - policy-enforced execution
|
|
5185
5506
|
# Ensure accessible working directory
|
|
@@ -5195,7 +5516,7 @@ exec /opt/agenshield/bin/shield-client skill run "${name}" "$@"
|
|
|
5195
5516
|
}
|
|
5196
5517
|
}
|
|
5197
5518
|
function removeSkillWrapper(name, binDir) {
|
|
5198
|
-
const wrapperPath =
|
|
5519
|
+
const wrapperPath = path10.join(binDir, name);
|
|
5199
5520
|
try {
|
|
5200
5521
|
if (fs10.existsSync(wrapperPath)) {
|
|
5201
5522
|
fs10.unlinkSync(wrapperPath);
|
|
@@ -5232,11 +5553,11 @@ function removeSkillPolicy(name) {
|
|
|
5232
5553
|
|
|
5233
5554
|
// libs/shield-daemon/src/services/openclaw-config.ts
|
|
5234
5555
|
import * as fs11 from "node:fs";
|
|
5235
|
-
import * as
|
|
5556
|
+
import * as path11 from "node:path";
|
|
5236
5557
|
import { execSync as execSync7 } from "node:child_process";
|
|
5237
5558
|
function getOpenClawConfigPath() {
|
|
5238
5559
|
const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
|
|
5239
|
-
return
|
|
5560
|
+
return path11.join(agentHome, ".openclaw", "openclaw.json");
|
|
5240
5561
|
}
|
|
5241
5562
|
function readConfig() {
|
|
5242
5563
|
const configPath = getOpenClawConfigPath();
|
|
@@ -5251,10 +5572,10 @@ function readConfig() {
|
|
|
5251
5572
|
}
|
|
5252
5573
|
function writeConfig(config) {
|
|
5253
5574
|
const configPath = getOpenClawConfigPath();
|
|
5254
|
-
fs11.mkdirSync(
|
|
5575
|
+
fs11.mkdirSync(path11.dirname(configPath), { recursive: true });
|
|
5255
5576
|
fs11.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
5256
5577
|
const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
|
|
5257
|
-
const brokerUser =
|
|
5578
|
+
const brokerUser = path11.basename(agentHome) + "_broker";
|
|
5258
5579
|
const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
|
|
5259
5580
|
try {
|
|
5260
5581
|
execSync7(`chown ${brokerUser}:${socketGroup} "${configPath}"`, { stdio: "pipe" });
|
|
@@ -5288,7 +5609,7 @@ function removeSkillEntry(slug) {
|
|
|
5288
5609
|
|
|
5289
5610
|
// libs/shield-daemon/src/services/marketplace.ts
|
|
5290
5611
|
import * as fs12 from "node:fs";
|
|
5291
|
-
import * as
|
|
5612
|
+
import * as path12 from "node:path";
|
|
5292
5613
|
import * as os4 from "node:os";
|
|
5293
5614
|
import { CONFIG_DIR as CONFIG_DIR2, MARKETPLACE_DIR } from "@agenshield/ipc";
|
|
5294
5615
|
var cache = /* @__PURE__ */ new Map();
|
|
@@ -5312,35 +5633,35 @@ var ANALYSIS_TIMEOUT = 4 * 6e4;
|
|
|
5312
5633
|
var SEARCH_CACHE_TTL = 6e4;
|
|
5313
5634
|
var DETAIL_CACHE_TTL = 5 * 6e4;
|
|
5314
5635
|
var SHORT_TIMEOUT = 1e4;
|
|
5315
|
-
async function convexAction(
|
|
5636
|
+
async function convexAction(path19, args, timeout) {
|
|
5316
5637
|
const res = await fetch(`${CONVEX_BASE}/api/action`, {
|
|
5317
5638
|
method: "POST",
|
|
5318
5639
|
signal: AbortSignal.timeout(timeout),
|
|
5319
5640
|
headers: { "Content-Type": "application/json" },
|
|
5320
|
-
body: JSON.stringify({ path:
|
|
5641
|
+
body: JSON.stringify({ path: path19, args, format: "json" })
|
|
5321
5642
|
});
|
|
5322
5643
|
if (!res.ok) {
|
|
5323
|
-
throw new Error(`Convex action ${
|
|
5644
|
+
throw new Error(`Convex action ${path19} returned ${res.status}`);
|
|
5324
5645
|
}
|
|
5325
5646
|
const body = await res.json();
|
|
5326
5647
|
if (body.status === "error") {
|
|
5327
|
-
throw new Error(`Convex action ${
|
|
5648
|
+
throw new Error(`Convex action ${path19}: ${body.errorMessage ?? "unknown error"}`);
|
|
5328
5649
|
}
|
|
5329
5650
|
return body.value;
|
|
5330
5651
|
}
|
|
5331
|
-
async function convexQuery(
|
|
5652
|
+
async function convexQuery(path19, args, timeout) {
|
|
5332
5653
|
const res = await fetch(`${CONVEX_BASE}/api/query`, {
|
|
5333
5654
|
method: "POST",
|
|
5334
5655
|
signal: AbortSignal.timeout(timeout),
|
|
5335
5656
|
headers: { "Content-Type": "application/json" },
|
|
5336
|
-
body: JSON.stringify({ path:
|
|
5657
|
+
body: JSON.stringify({ path: path19, args, format: "json" })
|
|
5337
5658
|
});
|
|
5338
5659
|
if (!res.ok) {
|
|
5339
|
-
throw new Error(`Convex query ${
|
|
5660
|
+
throw new Error(`Convex query ${path19} returned ${res.status}`);
|
|
5340
5661
|
}
|
|
5341
5662
|
const body = await res.json();
|
|
5342
5663
|
if (body.status === "error") {
|
|
5343
|
-
throw new Error(`Convex query ${
|
|
5664
|
+
throw new Error(`Convex query ${path19}: ${body.errorMessage ?? "unknown error"}`);
|
|
5344
5665
|
}
|
|
5345
5666
|
return body.value;
|
|
5346
5667
|
}
|
|
@@ -5401,7 +5722,7 @@ function isImageExt(filePath) {
|
|
|
5401
5722
|
}
|
|
5402
5723
|
var MAX_IMAGE_SIZE = 5e5;
|
|
5403
5724
|
function getMarketplaceDir() {
|
|
5404
|
-
return
|
|
5725
|
+
return path12.join(os4.homedir(), CONFIG_DIR2, MARKETPLACE_DIR);
|
|
5405
5726
|
}
|
|
5406
5727
|
async function downloadAndExtractZip(slug) {
|
|
5407
5728
|
const url = `${CLAWHUB_DOWNLOAD_BASE}/download?slug=${encodeURIComponent(slug)}`;
|
|
@@ -5439,20 +5760,20 @@ async function downloadAndExtractZip(slug) {
|
|
|
5439
5760
|
return files;
|
|
5440
5761
|
}
|
|
5441
5762
|
function storeDownloadedSkill(slug, meta, files) {
|
|
5442
|
-
const dir =
|
|
5443
|
-
const filesDir =
|
|
5763
|
+
const dir = path12.join(getMarketplaceDir(), slug);
|
|
5764
|
+
const filesDir = path12.join(dir, "files");
|
|
5444
5765
|
fs12.mkdirSync(filesDir, { recursive: true });
|
|
5445
5766
|
const fullMeta = { ...meta, downloadedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
5446
|
-
fs12.writeFileSync(
|
|
5767
|
+
fs12.writeFileSync(path12.join(dir, "metadata.json"), JSON.stringify(fullMeta, null, 2), "utf-8");
|
|
5447
5768
|
for (const file of files) {
|
|
5448
|
-
const filePath =
|
|
5449
|
-
fs12.mkdirSync(
|
|
5769
|
+
const filePath = path12.join(filesDir, file.name);
|
|
5770
|
+
fs12.mkdirSync(path12.dirname(filePath), { recursive: true });
|
|
5450
5771
|
fs12.writeFileSync(filePath, file.content, "utf-8");
|
|
5451
5772
|
}
|
|
5452
5773
|
console.log(`[Marketplace] Stored ${files.length} files for ${slug}`);
|
|
5453
5774
|
}
|
|
5454
5775
|
function updateDownloadedAnalysis(slug, analysis) {
|
|
5455
|
-
const metaPath =
|
|
5776
|
+
const metaPath = path12.join(getMarketplaceDir(), slug, "metadata.json");
|
|
5456
5777
|
try {
|
|
5457
5778
|
if (!fs12.existsSync(metaPath)) return;
|
|
5458
5779
|
const meta = JSON.parse(fs12.readFileSync(metaPath, "utf-8"));
|
|
@@ -5469,7 +5790,7 @@ function listDownloadedSkills() {
|
|
|
5469
5790
|
const entries = fs12.readdirSync(baseDir, { withFileTypes: true });
|
|
5470
5791
|
for (const entry of entries) {
|
|
5471
5792
|
if (!entry.isDirectory()) continue;
|
|
5472
|
-
const metaPath =
|
|
5793
|
+
const metaPath = path12.join(baseDir, entry.name, "metadata.json");
|
|
5473
5794
|
try {
|
|
5474
5795
|
const meta = JSON.parse(fs12.readFileSync(metaPath, "utf-8"));
|
|
5475
5796
|
results.push({
|
|
@@ -5488,7 +5809,7 @@ function listDownloadedSkills() {
|
|
|
5488
5809
|
return results;
|
|
5489
5810
|
}
|
|
5490
5811
|
function getDownloadedSkillFiles(slug) {
|
|
5491
|
-
const filesDir =
|
|
5812
|
+
const filesDir = path12.join(getMarketplaceDir(), slug, "files");
|
|
5492
5813
|
if (!fs12.existsSync(filesDir)) return [];
|
|
5493
5814
|
const files = [];
|
|
5494
5815
|
function walk(dir, prefix) {
|
|
@@ -5496,9 +5817,9 @@ function getDownloadedSkillFiles(slug) {
|
|
|
5496
5817
|
for (const entry of entries) {
|
|
5497
5818
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
5498
5819
|
if (entry.isDirectory()) {
|
|
5499
|
-
walk(
|
|
5820
|
+
walk(path12.join(dir, entry.name), rel);
|
|
5500
5821
|
} else {
|
|
5501
|
-
const content = fs12.readFileSync(
|
|
5822
|
+
const content = fs12.readFileSync(path12.join(dir, entry.name), "utf-8");
|
|
5502
5823
|
files.push({ name: rel, type: guessContentType(entry.name), content });
|
|
5503
5824
|
}
|
|
5504
5825
|
}
|
|
@@ -5507,7 +5828,7 @@ function getDownloadedSkillFiles(slug) {
|
|
|
5507
5828
|
return files;
|
|
5508
5829
|
}
|
|
5509
5830
|
function getDownloadedSkillMeta(slug) {
|
|
5510
|
-
const metaPath =
|
|
5831
|
+
const metaPath = path12.join(getMarketplaceDir(), slug, "metadata.json");
|
|
5511
5832
|
try {
|
|
5512
5833
|
if (fs12.existsSync(metaPath)) {
|
|
5513
5834
|
return JSON.parse(fs12.readFileSync(metaPath, "utf-8"));
|
|
@@ -6047,13 +6368,13 @@ function findSkillMdRecursive(dir, depth = 0) {
|
|
|
6047
6368
|
if (depth > 3) return null;
|
|
6048
6369
|
try {
|
|
6049
6370
|
for (const name of ["SKILL.md", "skill.md", "README.md", "readme.md"]) {
|
|
6050
|
-
const candidate =
|
|
6371
|
+
const candidate = path13.join(dir, name);
|
|
6051
6372
|
if (fs13.existsSync(candidate)) return candidate;
|
|
6052
6373
|
}
|
|
6053
6374
|
const entries = fs13.readdirSync(dir, { withFileTypes: true });
|
|
6054
6375
|
for (const entry of entries) {
|
|
6055
6376
|
if (!entry.isDirectory()) continue;
|
|
6056
|
-
const found = findSkillMdRecursive(
|
|
6377
|
+
const found = findSkillMdRecursive(path13.join(dir, entry.name), depth + 1);
|
|
6057
6378
|
if (found) return found;
|
|
6058
6379
|
}
|
|
6059
6380
|
} catch {
|
|
@@ -6096,9 +6417,9 @@ async function skillsRoutes(app) {
|
|
|
6096
6417
|
name: a.name,
|
|
6097
6418
|
source: "user",
|
|
6098
6419
|
status: "active",
|
|
6099
|
-
path:
|
|
6420
|
+
path: path13.join(skillsDir2 ?? "", a.name),
|
|
6100
6421
|
publisher: a.publisher,
|
|
6101
|
-
description: skillsDir2 ? readSkillDescription(
|
|
6422
|
+
description: skillsDir2 ? readSkillDescription(path13.join(skillsDir2, a.name)) : void 0
|
|
6102
6423
|
})),
|
|
6103
6424
|
// Quarantined
|
|
6104
6425
|
...quarantined.map((q) => ({
|
|
@@ -6113,8 +6434,8 @@ async function skillsRoutes(app) {
|
|
|
6113
6434
|
name,
|
|
6114
6435
|
source: "workspace",
|
|
6115
6436
|
status: "workspace",
|
|
6116
|
-
path:
|
|
6117
|
-
description: skillsDir2 ? readSkillDescription(
|
|
6437
|
+
path: path13.join(skillsDir2 ?? "", name),
|
|
6438
|
+
description: skillsDir2 ? readSkillDescription(path13.join(skillsDir2, name)) : void 0
|
|
6118
6439
|
})),
|
|
6119
6440
|
// Downloaded (not installed) → available
|
|
6120
6441
|
...availableDownloads.map((d) => ({
|
|
@@ -6155,15 +6476,15 @@ async function skillsRoutes(app) {
|
|
|
6155
6476
|
} else if (entry) {
|
|
6156
6477
|
source = "user";
|
|
6157
6478
|
status = "active";
|
|
6158
|
-
skillPath = skillsDir2 ?
|
|
6479
|
+
skillPath = skillsDir2 ? path13.join(skillsDir2, name) : "";
|
|
6159
6480
|
} else if (skillsDir2) {
|
|
6160
6481
|
source = "workspace";
|
|
6161
6482
|
status = "workspace";
|
|
6162
|
-
skillPath =
|
|
6483
|
+
skillPath = path13.join(skillsDir2, name);
|
|
6163
6484
|
}
|
|
6164
6485
|
let content = "";
|
|
6165
6486
|
let metadata;
|
|
6166
|
-
const dirToRead = skillPath || (skillsDir2 ?
|
|
6487
|
+
const dirToRead = skillPath || (skillsDir2 ? path13.join(skillsDir2, name) : "");
|
|
6167
6488
|
if (dirToRead) {
|
|
6168
6489
|
try {
|
|
6169
6490
|
const mdPath = findSkillMdRecursive(dirToRead);
|
|
@@ -6297,9 +6618,9 @@ async function skillsRoutes(app) {
|
|
|
6297
6618
|
return reply.code(500).send({ error: "Skills directory not configured" });
|
|
6298
6619
|
}
|
|
6299
6620
|
const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
|
|
6300
|
-
const binDir =
|
|
6621
|
+
const binDir = path13.join(agentHome, "bin");
|
|
6301
6622
|
const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
|
|
6302
|
-
const skillDir =
|
|
6623
|
+
const skillDir = path13.join(skillsDir2, name);
|
|
6303
6624
|
const isInstalled = fs13.existsSync(skillDir);
|
|
6304
6625
|
if (isInstalled) {
|
|
6305
6626
|
try {
|
|
@@ -6346,8 +6667,8 @@ async function skillsRoutes(app) {
|
|
|
6346
6667
|
} else {
|
|
6347
6668
|
fs13.mkdirSync(skillDir, { recursive: true });
|
|
6348
6669
|
for (const file of files) {
|
|
6349
|
-
const filePath =
|
|
6350
|
-
fs13.mkdirSync(
|
|
6670
|
+
const filePath = path13.join(skillDir, file.name);
|
|
6671
|
+
fs13.mkdirSync(path13.dirname(filePath), { recursive: true });
|
|
6351
6672
|
fs13.writeFileSync(filePath, file.content, "utf-8");
|
|
6352
6673
|
}
|
|
6353
6674
|
try {
|
|
@@ -6402,15 +6723,15 @@ async function skillsRoutes(app) {
|
|
|
6402
6723
|
return reply.code(500).send({ error: "Skills directory not configured" });
|
|
6403
6724
|
}
|
|
6404
6725
|
const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
|
|
6405
|
-
const binDir =
|
|
6726
|
+
const binDir = path13.join(agentHome, "bin");
|
|
6406
6727
|
const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
|
|
6407
|
-
const skillDir =
|
|
6728
|
+
const skillDir = path13.join(skillsDir2, name);
|
|
6408
6729
|
try {
|
|
6409
6730
|
addToApprovedList(name, publisher);
|
|
6410
6731
|
fs13.mkdirSync(skillDir, { recursive: true });
|
|
6411
6732
|
for (const file of files) {
|
|
6412
|
-
const filePath =
|
|
6413
|
-
fs13.mkdirSync(
|
|
6733
|
+
const filePath = path13.join(skillDir, file.name);
|
|
6734
|
+
fs13.mkdirSync(path13.dirname(filePath), { recursive: true });
|
|
6414
6735
|
fs13.writeFileSync(filePath, file.content, "utf-8");
|
|
6415
6736
|
}
|
|
6416
6737
|
try {
|
|
@@ -6440,7 +6761,7 @@ async function skillsRoutes(app) {
|
|
|
6440
6761
|
|
|
6441
6762
|
// libs/shield-daemon/src/routes/exec.ts
|
|
6442
6763
|
import * as fs14 from "node:fs";
|
|
6443
|
-
import * as
|
|
6764
|
+
import * as path14 from "node:path";
|
|
6444
6765
|
var ALLOWED_COMMANDS_PATH2 = "/opt/agenshield/config/allowed-commands.json";
|
|
6445
6766
|
var BIN_DIRS = [
|
|
6446
6767
|
"/usr/bin",
|
|
@@ -6464,7 +6785,7 @@ function loadConfig2() {
|
|
|
6464
6785
|
}
|
|
6465
6786
|
}
|
|
6466
6787
|
function saveConfig2(config) {
|
|
6467
|
-
const dir =
|
|
6788
|
+
const dir = path14.dirname(ALLOWED_COMMANDS_PATH2);
|
|
6468
6789
|
if (!fs14.existsSync(dir)) {
|
|
6469
6790
|
fs14.mkdirSync(dir, { recursive: true });
|
|
6470
6791
|
}
|
|
@@ -6481,7 +6802,7 @@ function scanSystemBins() {
|
|
|
6481
6802
|
const entries = fs14.readdirSync(dir);
|
|
6482
6803
|
for (const entry of entries) {
|
|
6483
6804
|
if (seen.has(entry)) continue;
|
|
6484
|
-
const fullPath =
|
|
6805
|
+
const fullPath = path14.join(dir, entry);
|
|
6485
6806
|
try {
|
|
6486
6807
|
const stat = fs14.statSync(fullPath);
|
|
6487
6808
|
if (stat.isFile() && (stat.mode & 73) !== 0) {
|
|
@@ -6536,7 +6857,7 @@ async function execRoutes(app) {
|
|
|
6536
6857
|
};
|
|
6537
6858
|
}
|
|
6538
6859
|
for (const p of paths) {
|
|
6539
|
-
if (!
|
|
6860
|
+
if (!path14.isAbsolute(p)) {
|
|
6540
6861
|
return {
|
|
6541
6862
|
success: false,
|
|
6542
6863
|
error: {
|
|
@@ -6964,7 +7285,7 @@ async function secretsRoutes(app) {
|
|
|
6964
7285
|
|
|
6965
7286
|
// libs/shield-daemon/src/routes/marketplace.ts
|
|
6966
7287
|
import * as fs15 from "node:fs";
|
|
6967
|
-
import * as
|
|
7288
|
+
import * as path15 from "node:path";
|
|
6968
7289
|
async function marketplaceRoutes(app) {
|
|
6969
7290
|
app.get(
|
|
6970
7291
|
"/marketplace/search",
|
|
@@ -7160,9 +7481,9 @@ async function marketplaceRoutes(app) {
|
|
|
7160
7481
|
return reply.code(500).send({ error: "Skills directory not configured" });
|
|
7161
7482
|
}
|
|
7162
7483
|
const agentHome = process.env["AGENSHIELD_AGENT_HOME"] || "/Users/ash_default_agent";
|
|
7163
|
-
const binDir =
|
|
7484
|
+
const binDir = path15.join(agentHome, "bin");
|
|
7164
7485
|
const socketGroup = process.env["AGENSHIELD_SOCKET_GROUP"] || "clawshield";
|
|
7165
|
-
skillDir =
|
|
7486
|
+
skillDir = path15.join(skillsDir2, slug);
|
|
7166
7487
|
emitSkillInstallProgress(slug, "approve", "Pre-approving skill");
|
|
7167
7488
|
addToApprovedList(slug, publisher);
|
|
7168
7489
|
logs.push("Skill pre-approved");
|
|
@@ -7230,14 +7551,14 @@ async function marketplaceRoutes(app) {
|
|
|
7230
7551
|
|
|
7231
7552
|
// libs/shield-daemon/src/routes/fs.ts
|
|
7232
7553
|
import * as fs16 from "node:fs";
|
|
7233
|
-
import * as
|
|
7554
|
+
import * as path16 from "node:path";
|
|
7234
7555
|
import * as os5 from "node:os";
|
|
7235
7556
|
var MAX_ENTRIES = 200;
|
|
7236
7557
|
async function fsRoutes(app) {
|
|
7237
7558
|
app.get("/fs/browse", async (request) => {
|
|
7238
7559
|
const dirPath = request.query.path || os5.homedir();
|
|
7239
7560
|
const showHidden = request.query.showHidden === "true";
|
|
7240
|
-
const resolvedPath =
|
|
7561
|
+
const resolvedPath = path16.resolve(dirPath);
|
|
7241
7562
|
let dirents;
|
|
7242
7563
|
try {
|
|
7243
7564
|
dirents = fs16.readdirSync(resolvedPath, { withFileTypes: true });
|
|
@@ -7249,7 +7570,7 @@ async function fsRoutes(app) {
|
|
|
7249
7570
|
if (!showHidden && dirent.name.startsWith(".")) continue;
|
|
7250
7571
|
entries.push({
|
|
7251
7572
|
name: dirent.name,
|
|
7252
|
-
path:
|
|
7573
|
+
path: path16.join(resolvedPath, dirent.name),
|
|
7253
7574
|
type: dirent.isDirectory() ? "directory" : "file"
|
|
7254
7575
|
});
|
|
7255
7576
|
if (entries.length >= MAX_ENTRIES) break;
|
|
@@ -7264,7 +7585,8 @@ async function fsRoutes(app) {
|
|
|
7264
7585
|
|
|
7265
7586
|
// libs/shield-daemon/src/services/activity-log.ts
|
|
7266
7587
|
import * as fs17 from "node:fs";
|
|
7267
|
-
import * as
|
|
7588
|
+
import * as path17 from "node:path";
|
|
7589
|
+
init_paths();
|
|
7268
7590
|
var ACTIVITY_FILE = "activity.jsonl";
|
|
7269
7591
|
var MAX_SIZE_BYTES = 100 * 1024 * 1024;
|
|
7270
7592
|
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -7281,7 +7603,7 @@ var ActivityLog = class {
|
|
|
7281
7603
|
writeCount = 0;
|
|
7282
7604
|
unsubscribe;
|
|
7283
7605
|
constructor() {
|
|
7284
|
-
this.filePath =
|
|
7606
|
+
this.filePath = path17.join(getConfigDir(), ACTIVITY_FILE);
|
|
7285
7607
|
}
|
|
7286
7608
|
/** Read historical events from the JSONL file, newest first */
|
|
7287
7609
|
getHistory(limit = 500) {
|
|
@@ -7381,26 +7703,31 @@ function globToRegex(pattern) {
|
|
|
7381
7703
|
const regexPattern = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, ".").replace(/{{GLOBSTAR}}/g, ".*");
|
|
7382
7704
|
return new RegExp(`^${regexPattern}$`, "i");
|
|
7383
7705
|
}
|
|
7384
|
-
function
|
|
7706
|
+
function normalizeUrlBase(pattern) {
|
|
7385
7707
|
let p = pattern.trim();
|
|
7386
7708
|
p = p.replace(/\/+$/, "");
|
|
7387
7709
|
if (!p.match(/^(\*|https?):\/\//i)) {
|
|
7388
7710
|
p = `https://${p}`;
|
|
7389
7711
|
}
|
|
7390
|
-
if (!p.endsWith("*")) {
|
|
7391
|
-
p = `${p}/**`;
|
|
7392
|
-
}
|
|
7393
7712
|
return p;
|
|
7394
7713
|
}
|
|
7714
|
+
function matchUrlPattern(pattern, target) {
|
|
7715
|
+
const base = normalizeUrlBase(pattern);
|
|
7716
|
+
const trimmed = pattern.trim().replace(/\/+$/, "");
|
|
7717
|
+
if (trimmed.endsWith("*")) {
|
|
7718
|
+
return globToRegex(base).test(target);
|
|
7719
|
+
}
|
|
7720
|
+
return globToRegex(base).test(target) || globToRegex(`${base}/**`).test(target);
|
|
7721
|
+
}
|
|
7395
7722
|
function normalizeUrlTarget(url) {
|
|
7396
7723
|
const trimmed = url.trim();
|
|
7397
7724
|
try {
|
|
7398
7725
|
const parsed = new URL(trimmed);
|
|
7399
|
-
let
|
|
7400
|
-
if (
|
|
7401
|
-
|
|
7726
|
+
let path19 = parsed.pathname;
|
|
7727
|
+
if (path19.length > 1) {
|
|
7728
|
+
path19 = path19.replace(/\/+$/, "");
|
|
7402
7729
|
}
|
|
7403
|
-
return `${parsed.protocol}//${parsed.host}${
|
|
7730
|
+
return `${parsed.protocol}//${parsed.host}${path19}${parsed.search}`;
|
|
7404
7731
|
} catch {
|
|
7405
7732
|
return trimmed.replace(/\/+$/, "");
|
|
7406
7733
|
}
|
|
@@ -7432,10 +7759,8 @@ function evaluatePolicyCheck(operation, target) {
|
|
|
7432
7759
|
if (policy.target !== "url" || policy.action !== "allow") continue;
|
|
7433
7760
|
for (const pattern of policy.patterns) {
|
|
7434
7761
|
if (!pattern.match(/^http:\/\//i)) continue;
|
|
7435
|
-
const effectivePattern = normalizeUrlPattern(pattern);
|
|
7436
7762
|
const effectiveTarget = normalizeUrlTarget(target);
|
|
7437
|
-
|
|
7438
|
-
if (regex.test(effectiveTarget)) {
|
|
7763
|
+
if (matchUrlPattern(pattern, effectiveTarget)) {
|
|
7439
7764
|
explicitHttpAllow = true;
|
|
7440
7765
|
break;
|
|
7441
7766
|
}
|
|
@@ -7457,11 +7782,15 @@ function evaluatePolicyCheck(operation, target) {
|
|
|
7457
7782
|
}
|
|
7458
7783
|
console.log("[policy_check] checking policy:", policy.name, "target:", policy.target, "action:", policy.action);
|
|
7459
7784
|
for (const pattern of policy.patterns) {
|
|
7460
|
-
const effectivePattern = targetType === "url" ? normalizeUrlPattern(pattern) : pattern;
|
|
7461
7785
|
const effectiveTarget = targetType === "url" ? normalizeUrlTarget(target) : target;
|
|
7462
|
-
|
|
7463
|
-
|
|
7464
|
-
|
|
7786
|
+
let matches;
|
|
7787
|
+
if (targetType === "url") {
|
|
7788
|
+
matches = matchUrlPattern(pattern, effectiveTarget);
|
|
7789
|
+
} else {
|
|
7790
|
+
const regex = globToRegex(pattern);
|
|
7791
|
+
matches = regex.test(effectiveTarget);
|
|
7792
|
+
}
|
|
7793
|
+
console.log("[policy_check] pattern:", pattern, "-> base:", targetType === "url" ? normalizeUrlBase(pattern) : pattern, "| target:", effectiveTarget, "| matches:", matches);
|
|
7465
7794
|
if (matches) {
|
|
7466
7795
|
console.log("[policy_check] MATCHED policy:", policy.name, "action:", policy.action);
|
|
7467
7796
|
return {
|
|
@@ -7635,20 +7964,20 @@ async function registerRoutes(app) {
|
|
|
7635
7964
|
|
|
7636
7965
|
// libs/shield-daemon/src/static.ts
|
|
7637
7966
|
import * as fs18 from "node:fs";
|
|
7638
|
-
import * as
|
|
7967
|
+
import * as path18 from "node:path";
|
|
7639
7968
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
7640
7969
|
var __filename = fileURLToPath2(import.meta.url);
|
|
7641
|
-
var __dirname =
|
|
7970
|
+
var __dirname = path18.dirname(__filename);
|
|
7642
7971
|
function getUiAssetsPath() {
|
|
7643
|
-
const pkgRootPath =
|
|
7972
|
+
const pkgRootPath = path18.join(__dirname, "..", "ui-assets");
|
|
7644
7973
|
if (fs18.existsSync(pkgRootPath)) {
|
|
7645
7974
|
return pkgRootPath;
|
|
7646
7975
|
}
|
|
7647
|
-
const bundledPath =
|
|
7976
|
+
const bundledPath = path18.join(__dirname, "ui-assets");
|
|
7648
7977
|
if (fs18.existsSync(bundledPath)) {
|
|
7649
7978
|
return bundledPath;
|
|
7650
7979
|
}
|
|
7651
|
-
const devPath =
|
|
7980
|
+
const devPath = path18.join(__dirname, "..", "..", "..", "dist", "apps", "shield-ui");
|
|
7652
7981
|
if (fs18.existsSync(devPath)) {
|
|
7653
7982
|
return devPath;
|
|
7654
7983
|
}
|
|
@@ -7748,6 +8077,17 @@ async function startServer(config) {
|
|
|
7748
8077
|
}, 3e4);
|
|
7749
8078
|
const activityLog = getActivityLog();
|
|
7750
8079
|
activityLog.start();
|
|
8080
|
+
try {
|
|
8081
|
+
const { loadConfig: loadDaemonConfig } = await Promise.resolve().then(() => (init_loader(), loader_exports));
|
|
8082
|
+
const { loadState: loadState2 } = await Promise.resolve().then(() => (init_state(), state_exports));
|
|
8083
|
+
const { syncCommandPoliciesAndWrappers: syncCommandPoliciesAndWrappers2 } = await Promise.resolve().then(() => (init_command_sync(), command_sync_exports));
|
|
8084
|
+
const cfg = loadDaemonConfig();
|
|
8085
|
+
const st = loadState2();
|
|
8086
|
+
syncCommandPoliciesAndWrappers2(cfg.policies, st, app.log);
|
|
8087
|
+
app.log.info("[startup] boot-time command-sync completed");
|
|
8088
|
+
} catch (err) {
|
|
8089
|
+
app.log.warn(`[startup] boot-time command-sync failed: ${err.message}`);
|
|
8090
|
+
}
|
|
7751
8091
|
try {
|
|
7752
8092
|
const vault = getVault();
|
|
7753
8093
|
const agenco = await vault.get("agenco");
|