failproofai 1.0.2-beta.8 → 1.0.2
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/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +4 -4
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/api/audit/invite/route.js +3 -2
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -3
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js +2 -2
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +4 -3
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js +4 -4
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js +4 -4
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
- package/.next/standalone/.next/server/app/policies/page.js +4 -4
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -4
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/[externals]__0-9ti7z._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[externals]__1lt47k4._.js → [externals]__0sg9-3p._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[externals]__0u3wrvq._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__01eqny5._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1p8poxd._.js → [root-of-the-server]__0igxt32._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zlqo2t._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1anxb5k._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1le9jqc._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1p-qi2t._.js +5 -0
- package/.next/standalone/.next/server/chunks/_09dz7xv._.js +27 -0
- package/.next/standalone/.next/server/chunks/_0bs-v8p._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0tovk6q._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0trp3yc._.js +3 -0
- package/.next/standalone/.next/server/chunks/{_19uwam-._.js → _1-akg6w._.js} +2 -2
- package/.next/standalone/.next/server/chunks/_185gyk9._.js +3 -0
- package/.next/standalone/.next/server/chunks/_1ek68ln._.js +5 -7
- package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_09d5c79._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_04op_ou._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{node_modules_posthog-node_dist_entrypoints_index_node_mjs_1d2g73o._.js → node_modules_posthog-node_dist_entrypoints_index_node_mjs_0dr20uz._.js} +1 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_0iu54mz._.js +3 -0
- package/.next/standalone/.next/server/chunks/src_hooks_custom-hooks-loader_ts_0lnb3n3._.js +10 -0
- package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n_lxhg._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1yh7hlv._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0ehudfr._.js +23 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1n2ywfz._.js → _1es2j7i._.js} +4 -4
- package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1h33neq._.js → _1un1pug._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +9 -9
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1cv9_c4._.js +10 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware.js +1 -1
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +19 -19
- package/.next/standalone/.next/static/chunks/0nkpwjk01ei3t.js +1 -0
- package/.next/standalone/.next/static/chunks/{2iryw37qfqy5y.js → 0ofdelz65_517.js} +1 -1
- package/.next/standalone/.next/static/chunks/{34j7nmidlcuni.js → 12uhnpwgsmcev.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0i3a6wf-f_yky.js → 1_1eb_149xyt9.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3927pu4ss_l4k.js → 1tx9pnxyjrkoj.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1n04wzsaes73g.js → 1x0jrl3a4_i2m.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3r0udrtpn78-e.js → 1zpv6omeln7al.js} +1 -1
- package/.next/standalone/.next/static/chunks/{416zumre3fvfw.js → 20hbg-r3io_ip.js} +1 -1
- package/.next/standalone/.next/static/chunks/{11a_m8yksthtf.js → 32bhi32ym8yrz.js} +2 -2
- package/.next/standalone/.next/static/chunks/{0u2ojcw25c_np.js → 3fr78ugeb4tjc.js} +1 -1
- package/.next/standalone/app/actions/get-hooks-config.ts +8 -1
- package/.next/standalone/app/actions/pack-actions.ts +12 -1
- package/.next/standalone/app/policies/hooks-client.tsx +41 -4
- package/.next/standalone/fp-cloud-cli/tests/test_pagination.py +204 -0
- package/.next/standalone/package.json +9 -9
- package/.next/standalone/server.js +1 -1
- package/README.md +58 -13
- package/bin/failproofai.mjs +10 -1
- package/dist/cli.mjs +22429 -21292
- package/dist/worker.mjs +1247 -540
- package/package.json +9 -9
- package/src/audit/index.ts +24 -4
- package/src/hooks/builtin-policies.ts +933 -8
- package/src/hooks/custom-hooks-loader.ts +98 -8
- package/src/hooks/handler.ts +26 -14
- package/src/hooks/manager.ts +36 -2
- package/src/hooks/pack-cli.ts +415 -42
- package/src/hooks/pack-failclosed.ts +100 -4
- package/src/hooks/pack-manifest.ts +29 -0
- package/src/hooks/pack-param-key.ts +36 -0
- package/src/hooks/pack-store.ts +101 -9
- package/src/hooks/policy-catalog.ts +9 -1
- package/src/hooks/policy-evaluator.ts +77 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__06cuf1y._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_usx0j._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_zk340._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0jnjf2t._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0oy4kxb._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zf034w._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tg4pu6._.js +0 -3
- package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +0 -3
- package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0mgrjnx._.js +0 -10
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1-wzs7f._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1wlvpe5._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/_0l7zv8b._.js +0 -23
- package/.next/standalone/.next/static/chunks/1ch5l9t2p16y4.js +0 -1
- /package/.next/standalone/.next/static/{NgD5HvuyPxwgrpeIiafJL → 5-xDMxcnrcXDFPQiy3yhg}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{NgD5HvuyPxwgrpeIiafJL → 5-xDMxcnrcXDFPQiy3yhg}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{NgD5HvuyPxwgrpeIiafJL → 5-xDMxcnrcXDFPQiy3yhg}/_ssgManifest.js +0 -0
package/dist/worker.mjs
CHANGED
|
@@ -702,7 +702,10 @@ var init_policy_catalog = __esm(() => {
|
|
|
702
702
|
displayTitle: "Tried to disable, pause or modify failproofai itself",
|
|
703
703
|
impact: "An agent that can pause or remove enforcement can switch off every other policy.",
|
|
704
704
|
description: "Block failproofai CLI commands, self-pause and uninstallation",
|
|
705
|
-
match: {
|
|
705
|
+
match: {
|
|
706
|
+
events: ["PreToolUse", "PermissionRequest"],
|
|
707
|
+
toolNames: ["Bash", "Write", "Edit", "NotebookEdit"]
|
|
708
|
+
},
|
|
706
709
|
defaultEnabled: true,
|
|
707
710
|
alwaysOn: true,
|
|
708
711
|
category: "Dangerous Commands"
|
|
@@ -1278,6 +1281,415 @@ function stripShellQuoting(command) {
|
|
|
1278
1281
|
const ansiDecoded = joined.replace(/\$'((?:[^'\\]|\\.)*)'/g, (_, body) => decodeAnsiC(body));
|
|
1279
1282
|
return ansiDecoded.replace(/\\(.)/g, "$1").replace(/['"]/g, "");
|
|
1280
1283
|
}
|
|
1284
|
+
function gitSubcommand(args) {
|
|
1285
|
+
for (let i = 0;i < args.length; i++) {
|
|
1286
|
+
const arg = args[i];
|
|
1287
|
+
if (GIT_FLAGS_WITH_OPERANDS.has(arg)) {
|
|
1288
|
+
i++;
|
|
1289
|
+
continue;
|
|
1290
|
+
}
|
|
1291
|
+
if (arg.startsWith("-"))
|
|
1292
|
+
continue;
|
|
1293
|
+
return arg;
|
|
1294
|
+
}
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
function globCouldNameState(raw) {
|
|
1298
|
+
const stripped = raw.replace(/^[()]+|[()]+$/g, "");
|
|
1299
|
+
return globWordCouldNameState(raw) || stripped !== raw && globWordCouldNameState(stripped);
|
|
1300
|
+
}
|
|
1301
|
+
function globWordCouldNameState(token) {
|
|
1302
|
+
if (!GLOB_METACHAR_RE.test(token))
|
|
1303
|
+
return false;
|
|
1304
|
+
for (const word of expandBraces(token)) {
|
|
1305
|
+
if (wordNamesState(word))
|
|
1306
|
+
return true;
|
|
1307
|
+
}
|
|
1308
|
+
return false;
|
|
1309
|
+
}
|
|
1310
|
+
function expandBraces(token) {
|
|
1311
|
+
let words = [token];
|
|
1312
|
+
for (let round = 0;round < BRACE_EXPANSION_ROUNDS; round++) {
|
|
1313
|
+
const next = [];
|
|
1314
|
+
let expanded = false;
|
|
1315
|
+
for (const word of words) {
|
|
1316
|
+
const group = firstBraceGroup(word);
|
|
1317
|
+
if (!group) {
|
|
1318
|
+
next.push(word);
|
|
1319
|
+
continue;
|
|
1320
|
+
}
|
|
1321
|
+
expanded = true;
|
|
1322
|
+
for (const alternative of group.alternatives) {
|
|
1323
|
+
next.push(word.slice(0, group.start) + alternative + word.slice(group.end + 1));
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
if (!expanded)
|
|
1327
|
+
return next;
|
|
1328
|
+
if (next.length > BRACE_EXPANSION_LIMIT)
|
|
1329
|
+
return [collapseBraces(token)];
|
|
1330
|
+
words = next;
|
|
1331
|
+
}
|
|
1332
|
+
return words.map(collapseBraces);
|
|
1333
|
+
}
|
|
1334
|
+
function collapseBraces(word) {
|
|
1335
|
+
let out = "";
|
|
1336
|
+
let depth = 0;
|
|
1337
|
+
for (const ch of word) {
|
|
1338
|
+
if (ch === "{") {
|
|
1339
|
+
if (depth === 0)
|
|
1340
|
+
out += "*";
|
|
1341
|
+
depth++;
|
|
1342
|
+
continue;
|
|
1343
|
+
}
|
|
1344
|
+
if (ch === "}") {
|
|
1345
|
+
if (depth > 0)
|
|
1346
|
+
depth--;
|
|
1347
|
+
continue;
|
|
1348
|
+
}
|
|
1349
|
+
if (depth === 0)
|
|
1350
|
+
out += ch;
|
|
1351
|
+
}
|
|
1352
|
+
return out;
|
|
1353
|
+
}
|
|
1354
|
+
function firstBraceGroup(word) {
|
|
1355
|
+
const start = word.indexOf("{");
|
|
1356
|
+
if (start === -1)
|
|
1357
|
+
return null;
|
|
1358
|
+
let depth = 0;
|
|
1359
|
+
const alternatives = [];
|
|
1360
|
+
let current = "";
|
|
1361
|
+
for (let i = start;i < word.length; i++) {
|
|
1362
|
+
const ch = word[i];
|
|
1363
|
+
if (ch === "{") {
|
|
1364
|
+
depth++;
|
|
1365
|
+
if (depth === 1)
|
|
1366
|
+
continue;
|
|
1367
|
+
} else if (ch === "}") {
|
|
1368
|
+
depth--;
|
|
1369
|
+
if (depth === 0) {
|
|
1370
|
+
alternatives.push(current);
|
|
1371
|
+
return { start, end: i, alternatives };
|
|
1372
|
+
}
|
|
1373
|
+
} else if (ch === "," && depth === 1) {
|
|
1374
|
+
alternatives.push(current);
|
|
1375
|
+
current = "";
|
|
1376
|
+
continue;
|
|
1377
|
+
}
|
|
1378
|
+
current += ch;
|
|
1379
|
+
}
|
|
1380
|
+
return null;
|
|
1381
|
+
}
|
|
1382
|
+
function wordNamesState(word) {
|
|
1383
|
+
if (FAILPROOFAI_STATE_PATH_RE.test(word))
|
|
1384
|
+
return true;
|
|
1385
|
+
const segments = word.split("/");
|
|
1386
|
+
for (let end = 1;end <= segments.length; end++) {
|
|
1387
|
+
if (globPrefixNamesState(segments.slice(0, end).join("/")))
|
|
1388
|
+
return true;
|
|
1389
|
+
}
|
|
1390
|
+
return false;
|
|
1391
|
+
}
|
|
1392
|
+
function compileGlobPattern(token) {
|
|
1393
|
+
let pattern = "";
|
|
1394
|
+
for (let i = 0;i < token.length; i++) {
|
|
1395
|
+
const extended = extglobAt(token, i);
|
|
1396
|
+
if (extended) {
|
|
1397
|
+
pattern += extended.source;
|
|
1398
|
+
i = extended.end;
|
|
1399
|
+
continue;
|
|
1400
|
+
}
|
|
1401
|
+
const ch = token[i];
|
|
1402
|
+
if (ch === "*")
|
|
1403
|
+
pattern += "[^/]*";
|
|
1404
|
+
else if (ch === "?")
|
|
1405
|
+
pattern += "[^/]";
|
|
1406
|
+
else if (ch === "[") {
|
|
1407
|
+
const bracket = bracketExpressionAt(token, i);
|
|
1408
|
+
if (!bracket) {
|
|
1409
|
+
pattern += "\\[";
|
|
1410
|
+
continue;
|
|
1411
|
+
}
|
|
1412
|
+
pattern += bracket.source;
|
|
1413
|
+
i = bracket.end;
|
|
1414
|
+
} else
|
|
1415
|
+
pattern += ch.replace(/[.+^${}()|\\\]]/g, "\\$&");
|
|
1416
|
+
}
|
|
1417
|
+
return pattern;
|
|
1418
|
+
}
|
|
1419
|
+
function extglobAt(token, start) {
|
|
1420
|
+
const operator = token[start];
|
|
1421
|
+
if (!"?*+@!".includes(operator) || token[start + 1] !== "(")
|
|
1422
|
+
return null;
|
|
1423
|
+
const alternatives = [];
|
|
1424
|
+
let current = "";
|
|
1425
|
+
let depth = 0;
|
|
1426
|
+
let i = start + 1;
|
|
1427
|
+
for (;i < token.length; i++) {
|
|
1428
|
+
const ch = token[i];
|
|
1429
|
+
if (ch === "(") {
|
|
1430
|
+
depth++;
|
|
1431
|
+
if (depth === 1)
|
|
1432
|
+
continue;
|
|
1433
|
+
} else if (ch === ")") {
|
|
1434
|
+
depth--;
|
|
1435
|
+
if (depth === 0) {
|
|
1436
|
+
alternatives.push(current);
|
|
1437
|
+
break;
|
|
1438
|
+
}
|
|
1439
|
+
} else if (ch === "|" && depth === 1) {
|
|
1440
|
+
alternatives.push(current);
|
|
1441
|
+
current = "";
|
|
1442
|
+
continue;
|
|
1443
|
+
}
|
|
1444
|
+
current += ch;
|
|
1445
|
+
}
|
|
1446
|
+
if (i >= token.length)
|
|
1447
|
+
return null;
|
|
1448
|
+
if (operator === "!")
|
|
1449
|
+
return { source: "[^/]*", end: i };
|
|
1450
|
+
const body = alternatives.map((alternative) => compileGlobPattern(alternative)).join("|");
|
|
1451
|
+
const quantifier = operator === "?" ? "?" : operator === "*" ? "*" : operator === "+" ? "+" : "";
|
|
1452
|
+
return { source: `(?:${body})${quantifier}`, end: i };
|
|
1453
|
+
}
|
|
1454
|
+
function bracketExpressionAt(token, start) {
|
|
1455
|
+
let i = start + 1;
|
|
1456
|
+
let negated = false;
|
|
1457
|
+
if (token[i] === "!" || token[i] === "^") {
|
|
1458
|
+
negated = true;
|
|
1459
|
+
i++;
|
|
1460
|
+
}
|
|
1461
|
+
let body = "";
|
|
1462
|
+
if (token[i] === "]") {
|
|
1463
|
+
body += "]";
|
|
1464
|
+
i++;
|
|
1465
|
+
}
|
|
1466
|
+
while (i < token.length && token[i] !== "]") {
|
|
1467
|
+
const inner = token[i] === "[" ? token[i + 1] : undefined;
|
|
1468
|
+
if (inner === ":" || inner === "." || inner === "=") {
|
|
1469
|
+
const close = token.indexOf(`${inner}]`, i + 2);
|
|
1470
|
+
if (close !== -1) {
|
|
1471
|
+
body += token.slice(i, close + 2);
|
|
1472
|
+
i = close + 2;
|
|
1473
|
+
continue;
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
body += token[i];
|
|
1477
|
+
i++;
|
|
1478
|
+
}
|
|
1479
|
+
if (i >= token.length)
|
|
1480
|
+
return null;
|
|
1481
|
+
if (/\[[.=:]/.test(body))
|
|
1482
|
+
return { source: "[^/]", end: i };
|
|
1483
|
+
const escaped = body.replace(/[\\\]]/g, "\\$&");
|
|
1484
|
+
if (escaped.length === 0)
|
|
1485
|
+
return { source: "[^/]", end: i };
|
|
1486
|
+
return { source: `[${negated ? "^" : ""}${escaped}]`, end: i };
|
|
1487
|
+
}
|
|
1488
|
+
function globPrefixNamesState(token) {
|
|
1489
|
+
if (!GLOB_METACHAR_RE.test(token))
|
|
1490
|
+
return false;
|
|
1491
|
+
const pattern = "^" + compileGlobPattern(token);
|
|
1492
|
+
let re;
|
|
1493
|
+
try {
|
|
1494
|
+
re = new RegExp(pattern + "$");
|
|
1495
|
+
} catch {
|
|
1496
|
+
return false;
|
|
1497
|
+
}
|
|
1498
|
+
if (!STATE_GLOB_CANDIDATES.some((candidate) => re.test(candidate)))
|
|
1499
|
+
return false;
|
|
1500
|
+
return !GLOB_DECOYS.some((decoy) => re.test(decoy));
|
|
1501
|
+
}
|
|
1502
|
+
function substitutionBodies(command) {
|
|
1503
|
+
const bodies = [];
|
|
1504
|
+
for (let i = 0;i < command.length; i++) {
|
|
1505
|
+
if (command[i] === "`") {
|
|
1506
|
+
const close = command.indexOf("`", i + 1);
|
|
1507
|
+
if (close === -1)
|
|
1508
|
+
break;
|
|
1509
|
+
bodies.push(command.slice(i + 1, close));
|
|
1510
|
+
i = close;
|
|
1511
|
+
continue;
|
|
1512
|
+
}
|
|
1513
|
+
if (command[i] !== "$" || command[i + 1] !== "(")
|
|
1514
|
+
continue;
|
|
1515
|
+
let depth = 1;
|
|
1516
|
+
let j = i + 2;
|
|
1517
|
+
for (;j < command.length && depth > 0; j++) {
|
|
1518
|
+
if (command[j] === "(")
|
|
1519
|
+
depth++;
|
|
1520
|
+
else if (command[j] === ")")
|
|
1521
|
+
depth--;
|
|
1522
|
+
}
|
|
1523
|
+
if (depth === 0)
|
|
1524
|
+
bodies.push(command.slice(i + 2, j - 1));
|
|
1525
|
+
i = j - 1;
|
|
1526
|
+
}
|
|
1527
|
+
return bodies;
|
|
1528
|
+
}
|
|
1529
|
+
function bareToken(token) {
|
|
1530
|
+
return token.replace(/^[(){}'"]+|[(){}'"]+$/g, "");
|
|
1531
|
+
}
|
|
1532
|
+
function unquotedToken(token) {
|
|
1533
|
+
return token.replace(/^['"]+|['"]+$/g, "");
|
|
1534
|
+
}
|
|
1535
|
+
function commandBasename(token) {
|
|
1536
|
+
const bare = bareToken(token);
|
|
1537
|
+
return bare.slice(bare.lastIndexOf("/") + 1);
|
|
1538
|
+
}
|
|
1539
|
+
function commandWords(simpleCommand) {
|
|
1540
|
+
const tokens = simpleCommand.trim().split(/\s+/).map(bareToken).filter(Boolean);
|
|
1541
|
+
let i = 0;
|
|
1542
|
+
while (i < tokens.length) {
|
|
1543
|
+
const token = tokens[i];
|
|
1544
|
+
if (/^\d*(?:>{1,2}\|?|<{1,3})/.test(token)) {
|
|
1545
|
+
i += /^\d*(?:>{1,2}\|?|<{1,3})$/.test(token) ? 2 : 1;
|
|
1546
|
+
continue;
|
|
1547
|
+
}
|
|
1548
|
+
const skippable = ENV_ASSIGNMENT_RE.test(token) || token.startsWith("-") || RUNNER_OPERAND_RE.test(token) || SHELL_KEYWORD_PREFIXES.has(token) || COMMAND_PREFIX_TOKENS.has(commandBasename(token));
|
|
1549
|
+
if (!skippable)
|
|
1550
|
+
break;
|
|
1551
|
+
i++;
|
|
1552
|
+
}
|
|
1553
|
+
return tokens.slice(i);
|
|
1554
|
+
}
|
|
1555
|
+
function simpleCommandDestroys(words, namesState) {
|
|
1556
|
+
if (words.length === 0)
|
|
1557
|
+
return false;
|
|
1558
|
+
const head = commandBasename(words[0]);
|
|
1559
|
+
const args = words.slice(1);
|
|
1560
|
+
if (head === "find") {
|
|
1561
|
+
if (args.some((a) => a === "-delete"))
|
|
1562
|
+
return true;
|
|
1563
|
+
if (args.some((a) => FIND_WRITE_ACTIONS.has(a)))
|
|
1564
|
+
return true;
|
|
1565
|
+
return args.some((a, i) => FIND_EXEC_ACTIONS.has(a) && !SAFE_EXEC_COMMANDS.has(commandBasename(args[i + 1] ?? "")));
|
|
1566
|
+
}
|
|
1567
|
+
if (head === "sed") {
|
|
1568
|
+
if (args.some((a) => a.startsWith("-i") || a.startsWith("--in-place")))
|
|
1569
|
+
return true;
|
|
1570
|
+
return args.some((a, i) => /(?:^|[;}/\s])w$/.test(a) && namesState(args[i + 1] ?? "")) || args.some((a) => /(?:^|[;}/\s])w\s*\S*\.failproofai/.test(a));
|
|
1571
|
+
}
|
|
1572
|
+
if (head === "awk" || head === "gawk" || head === "mawk") {
|
|
1573
|
+
return args.some((a) => /system\s*\(|\|\s*["']|\|&/.test(a));
|
|
1574
|
+
}
|
|
1575
|
+
if (COPY_COMMANDS.has(head)) {
|
|
1576
|
+
if (args.includes("--remove-source-files") && args.some((a) => namesState(a)))
|
|
1577
|
+
return true;
|
|
1578
|
+
const targetAt = args.findIndex((a) => a === "-t" || a === "--target-directory");
|
|
1579
|
+
if (targetAt !== -1 && namesState(args[targetAt + 1] ?? ""))
|
|
1580
|
+
return true;
|
|
1581
|
+
if (args.some((a) => a.startsWith("--target-directory=") && namesState(a)))
|
|
1582
|
+
return true;
|
|
1583
|
+
const operands = args.filter((a) => !a.startsWith("-"));
|
|
1584
|
+
const destination = operands[operands.length - 1];
|
|
1585
|
+
return destination !== undefined && namesState(destination);
|
|
1586
|
+
}
|
|
1587
|
+
if (OPERAND_WRITE_COMMANDS.has(head)) {
|
|
1588
|
+
return args.some((a) => !a.startsWith("-") && namesState(a));
|
|
1589
|
+
}
|
|
1590
|
+
if (SECOND_OPERAND_WRITERS.has(head)) {
|
|
1591
|
+
const operands = args.filter((a) => !a.startsWith("-"));
|
|
1592
|
+
return operands.length > 1 && namesState(operands[operands.length - 1]);
|
|
1593
|
+
}
|
|
1594
|
+
if (head === "git") {
|
|
1595
|
+
const subcommand = gitSubcommand(args);
|
|
1596
|
+
return subcommand !== undefined && GIT_DESTRUCTIVE_SUBCOMMANDS.has(subcommand);
|
|
1597
|
+
}
|
|
1598
|
+
if (writesViaOutputFlag(head, args, namesState))
|
|
1599
|
+
return true;
|
|
1600
|
+
if (STATE_READ_COMMANDS.has(head) || STATE_MENTION_COMMANDS.has(head))
|
|
1601
|
+
return false;
|
|
1602
|
+
return true;
|
|
1603
|
+
}
|
|
1604
|
+
function writesViaOutputFlag(head, args, namesState) {
|
|
1605
|
+
const flags = OUTPUT_FLAG_COMMANDS[head];
|
|
1606
|
+
if (!flags)
|
|
1607
|
+
return false;
|
|
1608
|
+
for (let i = 0;i < args.length; i++) {
|
|
1609
|
+
const arg = args[i];
|
|
1610
|
+
const long = flags.find((f) => f.startsWith("--") && arg.startsWith(`${f}=`));
|
|
1611
|
+
if (long && namesState(arg))
|
|
1612
|
+
return true;
|
|
1613
|
+
const bundled = /^-[A-Za-z]*[oD]$/.test(arg) && flags.includes(`-${arg[arg.length - 1]}`);
|
|
1614
|
+
if ((flags.includes(arg) || bundled) && namesState(args[i + 1] ?? ""))
|
|
1615
|
+
return true;
|
|
1616
|
+
}
|
|
1617
|
+
return false;
|
|
1618
|
+
}
|
|
1619
|
+
function destroysFailproofaiState(command, depth = 0) {
|
|
1620
|
+
if (depth === 0) {
|
|
1621
|
+
const bodies = substitutionBodies(command);
|
|
1622
|
+
for (let i = 0;i < bodies.length && i < 64; i++) {
|
|
1623
|
+
if (destroysFailproofaiState(bodies[i], 1))
|
|
1624
|
+
return true;
|
|
1625
|
+
bodies.push(...substitutionBodies(bodies[i]));
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
const assignments = [
|
|
1629
|
+
...command.matchAll(/(?:^|[\s;&|(])(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)=("[^"]*"|'[^']*'|[^\s;&|)]+)/g)
|
|
1630
|
+
].map(([, name, value]) => [name, value.replace(/^["']|["']$/g, "")]);
|
|
1631
|
+
const stateVars = new Set;
|
|
1632
|
+
for (let pass = 0;pass < assignments.length + 1; pass++) {
|
|
1633
|
+
const before = stateVars.size;
|
|
1634
|
+
for (const [name, value] of assignments) {
|
|
1635
|
+
const holdsState = FAILPROOFAI_STATE_PATH_RE.test(value) || [...value.matchAll(VAR_REFERENCE_RE)].some(([, ref]) => stateVars.has(ref));
|
|
1636
|
+
if (holdsState)
|
|
1637
|
+
stateVars.add(name);
|
|
1638
|
+
}
|
|
1639
|
+
if (stateVars.size === before)
|
|
1640
|
+
break;
|
|
1641
|
+
}
|
|
1642
|
+
const namesState = (text) => {
|
|
1643
|
+
if (FAILPROOFAI_STATE_PATH_RE.test(text))
|
|
1644
|
+
return true;
|
|
1645
|
+
for (const [, name] of text.matchAll(VAR_REFERENCE_RE)) {
|
|
1646
|
+
if (stateVars.has(name))
|
|
1647
|
+
return true;
|
|
1648
|
+
}
|
|
1649
|
+
return text.split(/\s+/).some((token) => globCouldNameState(unquotedToken(token)));
|
|
1650
|
+
};
|
|
1651
|
+
let cwdInState = false;
|
|
1652
|
+
let parenDepth = 0;
|
|
1653
|
+
let cdParenDepth = null;
|
|
1654
|
+
let loopCarriesState = false;
|
|
1655
|
+
for (const pipeline of command.split(PIPELINE_SEPARATORS)) {
|
|
1656
|
+
if (!pipeline.trim())
|
|
1657
|
+
continue;
|
|
1658
|
+
const simpleCommands = pipeline.split("|");
|
|
1659
|
+
const reachesState = namesState(pipeline) || cwdInState || loopCarriesState;
|
|
1660
|
+
if (reachesState && LOOP_HEADER_RE.test(pipeline))
|
|
1661
|
+
loopCarriesState = true;
|
|
1662
|
+
if (/(?:^|\s)done(?:\s|$)/.test(pipeline))
|
|
1663
|
+
loopCarriesState = false;
|
|
1664
|
+
if (reachesState) {
|
|
1665
|
+
for (const [, target] of pipeline.matchAll(REDIRECT_TARGET_RE)) {
|
|
1666
|
+
if (namesState(target.replace(/^["']|["']$/g, "")))
|
|
1667
|
+
return true;
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
for (const simple of simpleCommands) {
|
|
1671
|
+
parenDepth += (simple.match(/\(/g) ?? []).length;
|
|
1672
|
+
const words = commandWords(simple);
|
|
1673
|
+
const head = words.length > 0 ? commandBasename(words[0]) : "";
|
|
1674
|
+
if (head === "cd" || head === "pushd") {
|
|
1675
|
+
const operand = words.slice(1).find((w) => !w.startsWith("-"));
|
|
1676
|
+
cwdInState = operand !== undefined && namesState(operand);
|
|
1677
|
+
cdParenDepth = cwdInState ? parenDepth : null;
|
|
1678
|
+
} else if (head === "popd") {
|
|
1679
|
+
cwdInState = false;
|
|
1680
|
+
cdParenDepth = null;
|
|
1681
|
+
} else if (reachesState && words.length > 0 && simpleCommandDestroys(words, namesState)) {
|
|
1682
|
+
return true;
|
|
1683
|
+
}
|
|
1684
|
+
parenDepth -= (simple.match(/\)/g) ?? []).length;
|
|
1685
|
+
if (cdParenDepth !== null && parenDepth < cdParenDepth) {
|
|
1686
|
+
cwdInState = false;
|
|
1687
|
+
cdParenDepth = null;
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
return false;
|
|
1692
|
+
}
|
|
1281
1693
|
function statGitHeadMtimeMs(cwd) {
|
|
1282
1694
|
try {
|
|
1283
1695
|
return statSync3(join2(cwd, ".git", "HEAD")).mtimeMs;
|
|
@@ -1893,6 +2305,13 @@ function blockWorkOnMain(ctx) {
|
|
|
1893
2305
|
return allow();
|
|
1894
2306
|
}
|
|
1895
2307
|
function blockFailproofaiCommands(ctx) {
|
|
2308
|
+
if (ctx.toolName === "Write" || ctx.toolName === "Edit" || ctx.toolName === "NotebookEdit") {
|
|
2309
|
+
const path = getFilePath(ctx) || (ctx.toolInput?.notebook_path ?? "");
|
|
2310
|
+
if (FAILPROOFAI_STATE_PATH_RE.test(path)) {
|
|
2311
|
+
return deny("Writing to failproofai's own state would switch enforcement off. " + "If a policy is blocking legitimate work, say so and let the operator decide.");
|
|
2312
|
+
}
|
|
2313
|
+
return allow();
|
|
2314
|
+
}
|
|
1896
2315
|
if (ctx.toolName !== "Bash")
|
|
1897
2316
|
return allow();
|
|
1898
2317
|
const cmd = getCommand(ctx);
|
|
@@ -1907,8 +2326,8 @@ function blockFailproofaiCommands(ctx) {
|
|
|
1907
2326
|
if (FAILPROOFAI_UNINSTALL_RE.test(cmd) || FAILPROOFAI_UNINSTALL_RE.test(unescaped)) {
|
|
1908
2327
|
return deny("Uninstalling failproofai is blocked");
|
|
1909
2328
|
}
|
|
1910
|
-
if (
|
|
1911
|
-
return deny("Deleting or
|
|
2329
|
+
if (destroysFailproofaiState(cmd) || destroysFailproofaiState(unescaped)) {
|
|
2330
|
+
return deny("Deleting, moving or overwriting failproofai's own state would switch enforcement off. " + "If a policy is blocking legitimate work, say so and let the operator decide.");
|
|
1912
2331
|
}
|
|
1913
2332
|
return allow();
|
|
1914
2333
|
}
|
|
@@ -2368,7 +2787,7 @@ function registerBuiltinPolicies(enabledNames) {
|
|
|
2368
2787
|
}
|
|
2369
2788
|
}
|
|
2370
2789
|
}
|
|
2371
|
-
var isClaudeInternalPath, isClaudeSettingsFile, SHELL_OPERATORS, SHELL_METACHAR_RE, JWT_RE, API_KEY_PATTERNS, CONNECTION_STRING_RE, PRIVATE_KEY_RE, BEARER_TOKEN_RE, SECRET_PATTERNS, SQL_TOOL_RE, DESTRUCTIVE_SQL_RE, DELETE_NO_WHERE_RE, SQL_WHERE_RE, SCHEMA_ALTER_RE, PUBLISH_CMD_RE, ENV_PRINTENV_RE, ECHO_ENV_RE, EXPORT_RE, PS_ENV_VAR_RE, PS_CHILDITEM_ENV_RE, DOTNET_GETENV_RE, CMD_ECHO_ENV_RE, ENV_FILE_PATH_RE, ENV_CMD_RE, PS_ELEVATION_RE, RUNAS_RE, CURL_PIPE_SH_RE, SEGMENT_SEPARATORS, COMMAND_PREFIX_TOKENS, SELF_BINARY_TOKEN_RE, SELF_ENTRY_PATH_RE, CONFIG_SUBCOMMAND_RE, PAUSE_FLAG_RE, ENV_ASSIGNMENT_RE, RUNNER_OPERAND_RE, PS_WEB_PIPE_RE, SHORT_FLAG_BUNDLE_RE, SAFE_FORCE_PREFIXES, SECRET_FILE_RE, SECRET_FILE_ID_RSA_RE, SECRET_FILE_CREDENTIALS_RE, GIT_COMMIT_MERGE_RE,
|
|
2790
|
+
var isClaudeInternalPath, isClaudeSettingsFile, SHELL_OPERATORS, SHELL_METACHAR_RE, JWT_RE, API_KEY_PATTERNS, CONNECTION_STRING_RE, PRIVATE_KEY_RE, BEARER_TOKEN_RE, SECRET_PATTERNS, SQL_TOOL_RE, DESTRUCTIVE_SQL_RE, DELETE_NO_WHERE_RE, SQL_WHERE_RE, SCHEMA_ALTER_RE, PUBLISH_CMD_RE, ENV_PRINTENV_RE, ECHO_ENV_RE, EXPORT_RE, PS_ENV_VAR_RE, PS_CHILDITEM_ENV_RE, DOTNET_GETENV_RE, CMD_ECHO_ENV_RE, ENV_FILE_PATH_RE, ENV_CMD_RE, PS_ELEVATION_RE, RUNAS_RE, CURL_PIPE_SH_RE, SEGMENT_SEPARATORS, COMMAND_PREFIX_TOKENS, SELF_BINARY_TOKEN_RE, SELF_ENTRY_PATH_RE, CONFIG_SUBCOMMAND_RE, PAUSE_FLAG_RE, ENV_ASSIGNMENT_RE, RUNNER_OPERAND_RE, PS_WEB_PIPE_RE, SHORT_FLAG_BUNDLE_RE, SAFE_FORCE_PREFIXES, SECRET_FILE_RE, SECRET_FILE_ID_RSA_RE, SECRET_FILE_CREDENTIALS_RE, GIT_COMMIT_MERGE_RE, FAILPROOFAI_STATE_PATH_RE, STATE_READ_COMMANDS, SHELL_KEYWORD_PREFIXES, LOOP_HEADER_RE, COPY_COMMANDS, OPERAND_WRITE_COMMANDS, SECOND_OPERAND_WRITERS, OUTPUT_FLAG_COMMANDS, FIND_WRITE_ACTIONS, SAFE_EXEC_COMMANDS, GIT_DESTRUCTIVE_SUBCOMMANDS, GIT_FLAGS_WITH_OPERANDS, STATE_MENTION_COMMANDS, FIND_EXEC_ACTIONS, PIPELINE_SEPARATORS, VAR_REFERENCE_RE, STATE_GLOB_CANDIDATES, GLOB_DECOYS, BRACE_EXPANSION_LIMIT = 4096, BRACE_EXPANSION_ROUNDS = 24, GLOB_METACHAR_RE, REDIRECT_TARGET_RE, FAILPROOFAI_UNINSTALL_RE, GIT_AMEND_RE, GIT_STASH_DROP_RE, GIT_ADD_ALL_RE, NPM_GLOBAL_RE, YARN_GLOBAL_RE, PNPM_GLOBAL_RE, BUN_GLOBAL_RE, CARGO_INSTALL_RE, PIP_SYSTEM_RE, PKG_MANAGER_DETECTORS, NOHUP_RE, SCREEN_DETACH_RE, TMUX_DETACH_RE, DISOWN_RE, BACKGROUND_AMPERSAND_RE, KUBECTL_RE, TERRAFORM_RE, AWS_CLI_RE, GCLOUD_RE, AZ_CLI_RE, HELM_RE, GH_PIPELINE_RE, gitBranchCache, GIT_BRANCH_CACHE_MAX_ENTRIES = 500, EVAL_FLAG_RE, HOME_PREFIX_RE, RM_CMD_RE, FIND_CMD_RE, FIND_EXEC_RE, FIND_GLOBAL_OPT_RE, FIND_EXPR_START_RE, SCRATCH_ROOTS, CATASTROPHIC_DEPTH = 2, READ_LIKE_CMDS, TOOL_CALL_TRACKER_MAX_BYTES = 65536, SEGMENT_SPLIT_RE, POLICY_IMPLEMENTATIONS, BUILTIN_POLICIES;
|
|
2372
2791
|
var init_builtin_policies = __esm(() => {
|
|
2373
2792
|
init_policy_catalog();
|
|
2374
2793
|
init_hook_logger();
|
|
@@ -2464,7 +2883,175 @@ var init_builtin_policies = __esm(() => {
|
|
|
2464
2883
|
SECRET_FILE_ID_RSA_RE = /id_rsa/;
|
|
2465
2884
|
SECRET_FILE_CREDENTIALS_RE = /credentials/;
|
|
2466
2885
|
GIT_COMMIT_MERGE_RE = /git\s+(commit|merge|rebase|cherry-pick)\b/;
|
|
2467
|
-
|
|
2886
|
+
FAILPROOFAI_STATE_PATH_RE = /\.failproofai(?:\/|\b)/;
|
|
2887
|
+
STATE_READ_COMMANDS = new Set([
|
|
2888
|
+
"cat",
|
|
2889
|
+
"bat",
|
|
2890
|
+
"less",
|
|
2891
|
+
"more",
|
|
2892
|
+
"head",
|
|
2893
|
+
"tail",
|
|
2894
|
+
"nl",
|
|
2895
|
+
"strings",
|
|
2896
|
+
"xxd",
|
|
2897
|
+
"od",
|
|
2898
|
+
"jq",
|
|
2899
|
+
"yq",
|
|
2900
|
+
"diff",
|
|
2901
|
+
"cmp",
|
|
2902
|
+
"wc",
|
|
2903
|
+
"sort",
|
|
2904
|
+
"uniq",
|
|
2905
|
+
"column",
|
|
2906
|
+
"awk",
|
|
2907
|
+
"cut",
|
|
2908
|
+
"ls",
|
|
2909
|
+
"dir",
|
|
2910
|
+
"tree",
|
|
2911
|
+
"stat",
|
|
2912
|
+
"file",
|
|
2913
|
+
"du",
|
|
2914
|
+
"df",
|
|
2915
|
+
"readlink",
|
|
2916
|
+
"realpath",
|
|
2917
|
+
"basename",
|
|
2918
|
+
"dirname",
|
|
2919
|
+
"pwd",
|
|
2920
|
+
"test",
|
|
2921
|
+
"find",
|
|
2922
|
+
"grep",
|
|
2923
|
+
"egrep",
|
|
2924
|
+
"fgrep",
|
|
2925
|
+
"rg",
|
|
2926
|
+
"ag",
|
|
2927
|
+
"ack",
|
|
2928
|
+
"md5sum",
|
|
2929
|
+
"sha1sum",
|
|
2930
|
+
"sha256sum",
|
|
2931
|
+
"shasum",
|
|
2932
|
+
"cksum",
|
|
2933
|
+
"cd",
|
|
2934
|
+
"pushd",
|
|
2935
|
+
"popd",
|
|
2936
|
+
"cp",
|
|
2937
|
+
"rsync",
|
|
2938
|
+
"install",
|
|
2939
|
+
"tee",
|
|
2940
|
+
"sed",
|
|
2941
|
+
"true",
|
|
2942
|
+
"false",
|
|
2943
|
+
":",
|
|
2944
|
+
"echo",
|
|
2945
|
+
"printf",
|
|
2946
|
+
"[",
|
|
2947
|
+
"[[",
|
|
2948
|
+
"]",
|
|
2949
|
+
"]]",
|
|
2950
|
+
"test",
|
|
2951
|
+
"read",
|
|
2952
|
+
"for",
|
|
2953
|
+
"select",
|
|
2954
|
+
"case",
|
|
2955
|
+
"done",
|
|
2956
|
+
"fi",
|
|
2957
|
+
"esac"
|
|
2958
|
+
]);
|
|
2959
|
+
SHELL_KEYWORD_PREFIXES = new Set(["do", "then", "else", "elif", "if", "while", "until", "!"]);
|
|
2960
|
+
LOOP_HEADER_RE = /(?:^|[\s(])(?:while|until|for|select)\s/;
|
|
2961
|
+
COPY_COMMANDS = new Set(["cp", "rsync", "install"]);
|
|
2962
|
+
OPERAND_WRITE_COMMANDS = new Set(["tee"]);
|
|
2963
|
+
SECOND_OPERAND_WRITERS = new Set(["uniq", "xxd"]);
|
|
2964
|
+
OUTPUT_FLAG_COMMANDS = {
|
|
2965
|
+
sort: ["-o", "--output"],
|
|
2966
|
+
tree: ["-o", "--output"],
|
|
2967
|
+
curl: ["-o", "--output", "--output-dir", "-D", "--dump-header", "--trace", "--trace-ascii"]
|
|
2968
|
+
};
|
|
2969
|
+
FIND_WRITE_ACTIONS = new Set(["-fprint", "-fprint0", "-fprintf", "-fls"]);
|
|
2970
|
+
SAFE_EXEC_COMMANDS = new Set([
|
|
2971
|
+
"cat",
|
|
2972
|
+
"bat",
|
|
2973
|
+
"head",
|
|
2974
|
+
"tail",
|
|
2975
|
+
"nl",
|
|
2976
|
+
"strings",
|
|
2977
|
+
"xxd",
|
|
2978
|
+
"od",
|
|
2979
|
+
"jq",
|
|
2980
|
+
"yq",
|
|
2981
|
+
"wc",
|
|
2982
|
+
"ls",
|
|
2983
|
+
"stat",
|
|
2984
|
+
"file",
|
|
2985
|
+
"du",
|
|
2986
|
+
"readlink",
|
|
2987
|
+
"realpath",
|
|
2988
|
+
"basename",
|
|
2989
|
+
"dirname",
|
|
2990
|
+
"grep",
|
|
2991
|
+
"egrep",
|
|
2992
|
+
"fgrep",
|
|
2993
|
+
"rg",
|
|
2994
|
+
"md5sum",
|
|
2995
|
+
"sha1sum",
|
|
2996
|
+
"sha256sum",
|
|
2997
|
+
"shasum",
|
|
2998
|
+
"cksum",
|
|
2999
|
+
"echo",
|
|
3000
|
+
"printf",
|
|
3001
|
+
"true",
|
|
3002
|
+
":"
|
|
3003
|
+
]);
|
|
3004
|
+
GIT_DESTRUCTIVE_SUBCOMMANDS = new Set([
|
|
3005
|
+
"clean",
|
|
3006
|
+
"rm",
|
|
3007
|
+
"restore",
|
|
3008
|
+
"checkout",
|
|
3009
|
+
"reset",
|
|
3010
|
+
"stash"
|
|
3011
|
+
]);
|
|
3012
|
+
GIT_FLAGS_WITH_OPERANDS = new Set(["-C", "-c", "--git-dir", "--work-tree", "--namespace", "--exec-path"]);
|
|
3013
|
+
STATE_MENTION_COMMANDS = new Set(["git", "gh", "glab", "echo", "printf", "curl", "code", "open"]);
|
|
3014
|
+
FIND_EXEC_ACTIONS = new Set(["-exec", "-execdir", "-ok", "-okdir"]);
|
|
3015
|
+
PIPELINE_SEPARATORS = /\|\||&&|[;\n\r&]+/;
|
|
3016
|
+
VAR_REFERENCE_RE = /\$\{?([A-Za-z_][A-Za-z0-9_]*)\}?/g;
|
|
3017
|
+
STATE_GLOB_CANDIDATES = [
|
|
3018
|
+
".failproofai",
|
|
3019
|
+
"~/.failproofai",
|
|
3020
|
+
"$HOME/.failproofai",
|
|
3021
|
+
"${HOME}/.failproofai",
|
|
3022
|
+
"/root/.failproofai",
|
|
3023
|
+
"/home/u/.failproofai",
|
|
3024
|
+
"/Users/u/.failproofai"
|
|
3025
|
+
];
|
|
3026
|
+
GLOB_DECOYS = [
|
|
3027
|
+
".config",
|
|
3028
|
+
".cache",
|
|
3029
|
+
".local",
|
|
3030
|
+
".git",
|
|
3031
|
+
".npm",
|
|
3032
|
+
".ssh",
|
|
3033
|
+
".bashrc",
|
|
3034
|
+
"node_modules",
|
|
3035
|
+
"dist",
|
|
3036
|
+
"build",
|
|
3037
|
+
"target",
|
|
3038
|
+
"coverage",
|
|
3039
|
+
"tmp",
|
|
3040
|
+
"src",
|
|
3041
|
+
"~/.config",
|
|
3042
|
+
"~/.cache",
|
|
3043
|
+
"$HOME/.config",
|
|
3044
|
+
"${HOME}/.cache",
|
|
3045
|
+
"/home/u/.config",
|
|
3046
|
+
"/Users/u/.config",
|
|
3047
|
+
"/root/.cache",
|
|
3048
|
+
"/tmp/build",
|
|
3049
|
+
"a",
|
|
3050
|
+
"foo.txt",
|
|
3051
|
+
"test-failures"
|
|
3052
|
+
];
|
|
3053
|
+
GLOB_METACHAR_RE = /[*?[{]|[@+!]\(/;
|
|
3054
|
+
REDIRECT_TARGET_RE = /\d*>{1,2}\|?\s*("[^"]*"|'[^']*'|[^\s;&|<>]+)/g;
|
|
2468
3055
|
FAILPROOFAI_UNINSTALL_RE = /(?:npm\s+(?:uninstall|remove|un|r)\s.*failproofai|bun\s+remove\s.*failproofai|yarn\s+global\s+remove\s+failproofai|pnpm\s+(?:remove|uninstall|un)\s.*failproofai)/;
|
|
2469
3056
|
GIT_AMEND_RE = /\bgit\s+commit\b.*--amend\b/;
|
|
2470
3057
|
GIT_STASH_DROP_RE = /\bgit\s+stash\s+(?:drop|clear)\b/;
|
|
@@ -2558,87 +3145,416 @@ var init_builtin_policies = __esm(() => {
|
|
|
2558
3145
|
}));
|
|
2559
3146
|
});
|
|
2560
3147
|
|
|
2561
|
-
//
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
const raw =
|
|
2572
|
-
if (raw) {
|
|
2573
|
-
|
|
2574
|
-
return { kind: "current", version: raw };
|
|
2575
|
-
return raw.layout > LAYOUT_VERSION ? { kind: "future", found: raw.layout } : { kind: "stale", found: raw.layout };
|
|
3148
|
+
// src/hooks/cloud-managed-policies.ts
|
|
3149
|
+
import { createHash } from "node:crypto";
|
|
3150
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2, realpathSync, rmSync } from "node:fs";
|
|
3151
|
+
import { isAbsolute, relative, resolve as resolve4 } from "node:path";
|
|
3152
|
+
function cloudManagedPolicyRoot() {
|
|
3153
|
+
return process.env.FAILPROOFAI_CLOUD_POLICY_DIR ?? cloudPoliciesDir();
|
|
3154
|
+
}
|
|
3155
|
+
function parseManifest(value) {
|
|
3156
|
+
if (!value || typeof value !== "object")
|
|
3157
|
+
throw new Error("active manifest is not an object");
|
|
3158
|
+
const raw = value;
|
|
3159
|
+
if (!ACCEPTED_ACTIVE_SCHEMA_VERSIONS.includes(raw.schemaVersion)) {
|
|
3160
|
+
throw new Error(`unsupported active manifest schema ${String(raw.schemaVersion)} ` + `(supported: ${ACCEPTED_ACTIVE_SCHEMA_VERSIONS.join(", ")})`);
|
|
2576
3161
|
}
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
legacy.nextAudit(),
|
|
2581
|
-
legacy.auditSchedule()
|
|
2582
|
-
];
|
|
2583
|
-
if (layoutThreePositions.some((p) => existsSync3(p)))
|
|
2584
|
-
return { kind: "stale", found: 3 };
|
|
2585
|
-
return { kind: "current", version: { layout: LAYOUT_VERSION, cli: "" }, inferred: true };
|
|
3162
|
+
const deployment = raw.deployment ?? raw.generation;
|
|
3163
|
+
if (!Number.isSafeInteger(deployment) || (deployment ?? -1) < 0) {
|
|
3164
|
+
throw new Error("active manifest deployment is invalid");
|
|
2586
3165
|
}
|
|
2587
|
-
if (
|
|
2588
|
-
|
|
2589
|
-
const
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
legacy.cloudCredentials(),
|
|
2595
|
-
legacy.ingestCredentials(),
|
|
2596
|
-
legacy.auditDashboard()
|
|
2597
|
-
];
|
|
2598
|
-
return layoutOneMarkers.some((p) => existsSync3(p)) ? { kind: "stale", found: 1 } : { kind: "absent" };
|
|
3166
|
+
if (!Array.isArray(raw.policies))
|
|
3167
|
+
throw new Error("active manifest policies is not an array");
|
|
3168
|
+
const policies = raw.policies.map((p) => {
|
|
3169
|
+
const entry = p;
|
|
3170
|
+
return { ...entry, version: entry.version ?? entry.revision };
|
|
3171
|
+
});
|
|
3172
|
+
return { schemaVersion: raw.schemaVersion, deployment, policies };
|
|
2599
3173
|
}
|
|
2600
|
-
function
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
const
|
|
2605
|
-
|
|
3174
|
+
function resolveManagedPath(root, candidate) {
|
|
3175
|
+
if (!candidate || isAbsolute(candidate))
|
|
3176
|
+
throw new Error(`unsafe managed policy path ${JSON.stringify(candidate)}`);
|
|
3177
|
+
const absolute = resolve4(root, candidate);
|
|
3178
|
+
const lexicalRelative = relative(root, absolute);
|
|
3179
|
+
if (lexicalRelative.startsWith("..") || isAbsolute(lexicalRelative)) {
|
|
3180
|
+
throw new Error(`managed policy path escapes its root: ${JSON.stringify(candidate)}`);
|
|
3181
|
+
}
|
|
3182
|
+
const realRoot = realpathSync(root);
|
|
3183
|
+
const realFile = realpathSync(absolute);
|
|
3184
|
+
const physicalRelative = relative(realRoot, realFile);
|
|
3185
|
+
if (physicalRelative.startsWith("..") || isAbsolute(physicalRelative)) {
|
|
3186
|
+
throw new Error(`managed policy symlink escapes its root: ${JSON.stringify(candidate)}`);
|
|
3187
|
+
}
|
|
3188
|
+
return realFile;
|
|
2606
3189
|
}
|
|
2607
|
-
function
|
|
2608
|
-
|
|
3190
|
+
function readActiveCloudManagedPolicies() {
|
|
3191
|
+
const root = cloudManagedPolicyRoot();
|
|
3192
|
+
const activePath = resolve4(root, "active.json");
|
|
3193
|
+
if (!existsSync3(activePath))
|
|
3194
|
+
return [];
|
|
3195
|
+
let raw;
|
|
2609
3196
|
try {
|
|
2610
|
-
|
|
2611
|
-
} catch {
|
|
2612
|
-
|
|
3197
|
+
raw = JSON.parse(readFileSync2(activePath, "utf8"));
|
|
3198
|
+
} catch (err) {
|
|
3199
|
+
throw new Error(`failed to read cloud-managed active manifest: ${err instanceof Error ? err.message : String(err)}`);
|
|
2613
3200
|
}
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
if (!
|
|
2618
|
-
|
|
3201
|
+
const manifest = parseManifest(raw);
|
|
3202
|
+
const seen = new Set;
|
|
3203
|
+
return manifest.policies.map((policy) => {
|
|
3204
|
+
if (!POLICY_ID_RE.test(policy.id) || policy.id === "." || policy.id === "..") {
|
|
3205
|
+
throw new Error(`unsafe cloud-managed policy id ${JSON.stringify(policy.id)}`);
|
|
3206
|
+
}
|
|
3207
|
+
if (!Number.isSafeInteger(policy.version) || policy.version < 0) {
|
|
3208
|
+
throw new Error(`invalid version for cloud-managed policy ${policy.id}`);
|
|
3209
|
+
}
|
|
3210
|
+
if (!SHA256_RE.test(policy.sha256)) {
|
|
3211
|
+
throw new Error(`invalid SHA-256 for cloud-managed policy ${policy.id}`);
|
|
3212
|
+
}
|
|
3213
|
+
if (seen.has(policy.id))
|
|
3214
|
+
throw new Error(`duplicate cloud-managed policy id ${policy.id}`);
|
|
3215
|
+
seen.add(policy.id);
|
|
3216
|
+
const path = resolveManagedPath(root, policy.path);
|
|
3217
|
+
const bytes = readFileSync2(path);
|
|
3218
|
+
const actual = createHash("sha256").update(bytes).digest("hex");
|
|
3219
|
+
if (actual !== policy.sha256) {
|
|
3220
|
+
throw new Error(`cloud-managed policy ${policy.id} failed integrity verification: expected ${policy.sha256}, got ${actual}`);
|
|
3221
|
+
}
|
|
3222
|
+
if (policy.effect !== undefined && policy.effect !== "enforce" && policy.effect !== "observe") {
|
|
3223
|
+
throw new Error(`unknown effect ${JSON.stringify(policy.effect)} for cloud-managed policy ${policy.id}`);
|
|
3224
|
+
}
|
|
2619
3225
|
return {
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
3226
|
+
id: policy.id,
|
|
3227
|
+
version: policy.version,
|
|
3228
|
+
effect: policy.effect ?? "enforce",
|
|
3229
|
+
sha256: policy.sha256,
|
|
3230
|
+
path,
|
|
3231
|
+
deployment: manifest.deployment
|
|
2623
3232
|
};
|
|
2624
|
-
}
|
|
2625
|
-
return readLegacyTomlVersion(text);
|
|
2626
|
-
}
|
|
2627
|
-
}
|
|
2628
|
-
function readIntervalDays(raw) {
|
|
2629
|
-
if (typeof raw !== "number" || !Number.isFinite(raw))
|
|
2630
|
-
return DEFAULT_AUDIT_INTERVAL_DAYS;
|
|
2631
|
-
const days = Math.floor(raw);
|
|
2632
|
-
if (days < MIN_AUDIT_INTERVAL_DAYS)
|
|
2633
|
-
return DEFAULT_AUDIT_INTERVAL_DAYS;
|
|
2634
|
-
return Math.min(days, MAX_AUDIT_INTERVAL_DAYS);
|
|
3233
|
+
});
|
|
2635
3234
|
}
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
3235
|
+
var ACCEPTED_ACTIVE_SCHEMA_VERSIONS, SHA256_RE, POLICY_ID_RE;
|
|
3236
|
+
var init_cloud_managed_policies = __esm(() => {
|
|
3237
|
+
init_fp_home();
|
|
3238
|
+
ACCEPTED_ACTIVE_SCHEMA_VERSIONS = [1, 2];
|
|
3239
|
+
SHA256_RE = /^[a-f0-9]{64}$/;
|
|
3240
|
+
POLICY_ID_RE = /^[A-Za-z0-9._-]{1,128}$/;
|
|
3241
|
+
});
|
|
3242
|
+
|
|
3243
|
+
// src/hooks/pack-manifest.ts
|
|
3244
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
3245
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "node:fs";
|
|
3246
|
+
import { resolve as resolve5 } from "node:path";
|
|
3247
|
+
function packsRoot() {
|
|
3248
|
+
return process.env.FAILPROOFAI_PACK_DIR ?? packsDir();
|
|
3249
|
+
}
|
|
3250
|
+
function parsePackIdentity(value) {
|
|
3251
|
+
if (typeof value.id !== "string" || !PACK_ID_RE.test(value.id)) {
|
|
3252
|
+
throw new Error(`unsafe pack id ${JSON.stringify(value.id)}`);
|
|
3253
|
+
}
|
|
3254
|
+
if (typeof value.version !== "string" || !PACK_VERSION_RE.test(value.version)) {
|
|
3255
|
+
throw new Error(`invalid version for pack ${value.id}`);
|
|
3256
|
+
}
|
|
3257
|
+
if (value.effect !== undefined && value.effect !== "enforce" && value.effect !== "observe") {
|
|
3258
|
+
throw new Error(`unknown effect ${JSON.stringify(value.effect)} for pack ${value.id}`);
|
|
3259
|
+
}
|
|
3260
|
+
const commit = typeof value.commit === "string" && PACK_COMMIT_RE.test(value.commit.trim().toLowerCase()) ? value.commit.trim().toLowerCase() : undefined;
|
|
3261
|
+
return {
|
|
3262
|
+
id: value.id,
|
|
3263
|
+
version: value.version,
|
|
3264
|
+
effect: value.effect ?? "enforce",
|
|
3265
|
+
...commit ? { commit } : {}
|
|
3266
|
+
};
|
|
3267
|
+
}
|
|
3268
|
+
function hasInstalledPacks() {
|
|
3269
|
+
try {
|
|
3270
|
+
const raw = JSON.parse(readFileSync3(installedFilePath(), "utf8"));
|
|
3271
|
+
return Array.isArray(raw.packs) && raw.packs.length > 0;
|
|
3272
|
+
} catch {
|
|
3273
|
+
return false;
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
function installedFilePath() {
|
|
3277
|
+
return process.env.FAILPROOFAI_PACK_DIR ? resolve5(process.env.FAILPROOFAI_PACK_DIR, "installed.json") : packsInstalledFile();
|
|
3278
|
+
}
|
|
3279
|
+
function parsePackPolicy(packId, value, index) {
|
|
3280
|
+
const where = `${packId} policy #${index}`;
|
|
3281
|
+
if (!value || typeof value !== "object")
|
|
3282
|
+
throw new Error(`${where} is not an object`);
|
|
3283
|
+
const raw = value;
|
|
3284
|
+
const name = raw.name;
|
|
3285
|
+
if (typeof name !== "string" || !PACK_POLICY_NAME_RE.test(name)) {
|
|
3286
|
+
throw new Error(`${where} has an unsafe name ${JSON.stringify(name)}`);
|
|
3287
|
+
}
|
|
3288
|
+
if ("alwaysOn" in raw) {
|
|
3289
|
+
throw new Error(`${where} declares alwaysOn, which packs may not set`);
|
|
3290
|
+
}
|
|
3291
|
+
if (typeof raw.description !== "string" || raw.description.length === 0) {
|
|
3292
|
+
throw new Error(`${where} is missing a description`);
|
|
3293
|
+
}
|
|
3294
|
+
if (typeof raw.category !== "string" || raw.category.length === 0) {
|
|
3295
|
+
throw new Error(`${where} is missing a category`);
|
|
3296
|
+
}
|
|
3297
|
+
if (typeof raw.defaultEnabled !== "boolean") {
|
|
3298
|
+
throw new Error(`${where} is missing defaultEnabled`);
|
|
3299
|
+
}
|
|
3300
|
+
if (!raw.match || typeof raw.match !== "object") {
|
|
3301
|
+
throw new Error(`${where} is missing a match`);
|
|
3302
|
+
}
|
|
3303
|
+
const matchShape = raw.match;
|
|
3304
|
+
for (const key of ["events", "toolNames"]) {
|
|
3305
|
+
const value2 = matchShape[key];
|
|
3306
|
+
if (value2 === undefined)
|
|
3307
|
+
continue;
|
|
3308
|
+
if (!Array.isArray(value2) || value2.some((e) => typeof e !== "string" || e.length === 0)) {
|
|
3309
|
+
throw new Error(`${where} has a match.${key} that is not a list of names`);
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3312
|
+
return raw;
|
|
3313
|
+
}
|
|
3314
|
+
function parsePack(root, value) {
|
|
3315
|
+
if (!value || typeof value !== "object")
|
|
3316
|
+
throw new Error("pack entry is not an object");
|
|
3317
|
+
const raw = value;
|
|
3318
|
+
const identity = parsePackIdentity(raw);
|
|
3319
|
+
if (typeof raw.source !== "string" || raw.source.length === 0) {
|
|
3320
|
+
throw new Error(`pack ${raw.id} has no source`);
|
|
3321
|
+
}
|
|
3322
|
+
if (typeof raw.sha256 !== "string" || !SHA256_RE2.test(raw.sha256)) {
|
|
3323
|
+
throw new Error(`invalid SHA-256 for pack ${raw.id}`);
|
|
3324
|
+
}
|
|
3325
|
+
const effect = identity.effect;
|
|
3326
|
+
const path = resolveManagedPath(root, raw.entry);
|
|
3327
|
+
const actual = createHash2("sha256").update(readFileSync3(path)).digest("hex");
|
|
3328
|
+
if (actual !== raw.sha256) {
|
|
3329
|
+
throw new Error(`pack ${raw.id} failed integrity verification: expected ${raw.sha256}, got ${actual}`);
|
|
3330
|
+
}
|
|
3331
|
+
if (!Array.isArray(raw.policies))
|
|
3332
|
+
throw new Error(`pack ${raw.id} policies is not an array`);
|
|
3333
|
+
const policies = raw.policies.map((p, i) => parsePackPolicy(raw.id, p, i));
|
|
3334
|
+
const names = new Set;
|
|
3335
|
+
for (const p of policies) {
|
|
3336
|
+
if (names.has(p.name))
|
|
3337
|
+
throw new Error(`pack ${raw.id} declares ${p.name} twice`);
|
|
3338
|
+
names.add(p.name);
|
|
3339
|
+
}
|
|
3340
|
+
let enabled = null;
|
|
3341
|
+
if (raw.enabled !== undefined) {
|
|
3342
|
+
if (!Array.isArray(raw.enabled) || raw.enabled.some((n) => typeof n !== "string")) {
|
|
3343
|
+
throw new Error(`pack ${raw.id} enabled is not an array of names`);
|
|
3344
|
+
}
|
|
3345
|
+
enabled = raw.enabled.filter((n) => names.has(n));
|
|
3346
|
+
}
|
|
3347
|
+
let clis = null;
|
|
3348
|
+
if (raw.clis !== undefined) {
|
|
3349
|
+
if (!Array.isArray(raw.clis) || raw.clis.some((c) => typeof c !== "string")) {
|
|
3350
|
+
throw new Error(`pack ${raw.id} clis is not an array of agent names`);
|
|
3351
|
+
}
|
|
3352
|
+
clis = raw.clis;
|
|
3353
|
+
}
|
|
3354
|
+
return {
|
|
3355
|
+
clis,
|
|
3356
|
+
id: identity.id,
|
|
3357
|
+
version: identity.version,
|
|
3358
|
+
source: raw.source,
|
|
3359
|
+
path,
|
|
3360
|
+
sha256: raw.sha256,
|
|
3361
|
+
effect,
|
|
3362
|
+
policies,
|
|
3363
|
+
enabled
|
|
3364
|
+
};
|
|
3365
|
+
}
|
|
3366
|
+
function readInstalledPacks() {
|
|
3367
|
+
const manifestPath = installedFilePath();
|
|
3368
|
+
if (!existsSync4(manifestPath))
|
|
3369
|
+
return { packs: [], errors: [] };
|
|
3370
|
+
let raw;
|
|
3371
|
+
try {
|
|
3372
|
+
raw = JSON.parse(readFileSync3(manifestPath, "utf8"));
|
|
3373
|
+
} catch (err) {
|
|
3374
|
+
return {
|
|
3375
|
+
packs: [],
|
|
3376
|
+
errors: [{ id: null, reason: `unreadable pack manifest: ${errText(err)}` }]
|
|
3377
|
+
};
|
|
3378
|
+
}
|
|
3379
|
+
if (!raw || typeof raw !== "object") {
|
|
3380
|
+
return { packs: [], errors: [{ id: null, reason: "pack manifest is not an object" }] };
|
|
3381
|
+
}
|
|
3382
|
+
const manifest = raw;
|
|
3383
|
+
if (!ACCEPTED_PACK_SCHEMA_VERSIONS.includes(manifest.schemaVersion)) {
|
|
3384
|
+
return {
|
|
3385
|
+
packs: [],
|
|
3386
|
+
errors: [
|
|
3387
|
+
{
|
|
3388
|
+
id: null,
|
|
3389
|
+
reason: `unsupported pack manifest schema ${String(manifest.schemaVersion)} ` + `(supported: ${ACCEPTED_PACK_SCHEMA_VERSIONS.join(", ")})`
|
|
3390
|
+
}
|
|
3391
|
+
]
|
|
3392
|
+
};
|
|
3393
|
+
}
|
|
3394
|
+
if (!Array.isArray(manifest.packs)) {
|
|
3395
|
+
return { packs: [], errors: [{ id: null, reason: "pack manifest packs is not an array" }] };
|
|
3396
|
+
}
|
|
3397
|
+
const root = packsRoot();
|
|
3398
|
+
const packs = [];
|
|
3399
|
+
const errors = [];
|
|
3400
|
+
const seen = new Set;
|
|
3401
|
+
for (const entry of manifest.packs) {
|
|
3402
|
+
const declaredId = entry && typeof entry === "object" && typeof entry.id === "string" ? entry.id : null;
|
|
3403
|
+
try {
|
|
3404
|
+
const pack = parsePack(root, entry);
|
|
3405
|
+
if (seen.has(pack.id))
|
|
3406
|
+
throw new Error(`duplicate pack id ${pack.id}`);
|
|
3407
|
+
seen.add(pack.id);
|
|
3408
|
+
packs.push(pack);
|
|
3409
|
+
} catch (err) {
|
|
3410
|
+
const rec = entry;
|
|
3411
|
+
const declaredEffect = rec && (rec.effect === "observe" || rec.effect === "enforce") ? rec.effect : "enforce";
|
|
3412
|
+
errors.push({
|
|
3413
|
+
id: declaredId,
|
|
3414
|
+
reason: errText(err),
|
|
3415
|
+
effect: declaredEffect,
|
|
3416
|
+
...Array.isArray(rec?.policies) ? { declared: safeDeclared(rec.policies) } : {},
|
|
3417
|
+
...Array.isArray(rec?.clis) && rec.clis.every((c) => typeof c === "string") ? { clis: rec.clis } : {}
|
|
3418
|
+
});
|
|
3419
|
+
}
|
|
3420
|
+
}
|
|
3421
|
+
return { packs, errors };
|
|
3422
|
+
}
|
|
3423
|
+
function safeDeclared(raw) {
|
|
3424
|
+
const out = [];
|
|
3425
|
+
for (const value of raw) {
|
|
3426
|
+
if (!value || typeof value !== "object")
|
|
3427
|
+
continue;
|
|
3428
|
+
const p = value;
|
|
3429
|
+
if (typeof p.name !== "string" || !p.match || typeof p.match !== "object")
|
|
3430
|
+
continue;
|
|
3431
|
+
out.push(p);
|
|
3432
|
+
}
|
|
3433
|
+
return out;
|
|
3434
|
+
}
|
|
3435
|
+
function errText(err) {
|
|
3436
|
+
return err instanceof Error ? err.message : String(err);
|
|
3437
|
+
}
|
|
3438
|
+
var ACCEPTED_PACK_SCHEMA_VERSIONS, SHA256_RE2, PACK_ID_RE, PACK_VERSION_RE, PACK_COMMIT_RE, PACK_POLICY_NAME_RE;
|
|
3439
|
+
var init_pack_manifest = __esm(() => {
|
|
3440
|
+
init_fp_home();
|
|
3441
|
+
init_cloud_managed_policies();
|
|
3442
|
+
ACCEPTED_PACK_SCHEMA_VERSIONS = [1];
|
|
3443
|
+
SHA256_RE2 = /^[a-f0-9]{64}$/;
|
|
3444
|
+
PACK_ID_RE = /^[A-Za-z0-9._-]{1,64}\/[A-Za-z0-9._-]{1,64}$/;
|
|
3445
|
+
PACK_VERSION_RE = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,63}$/;
|
|
3446
|
+
PACK_COMMIT_RE = /^[0-9a-f]{7,40}$/;
|
|
3447
|
+
PACK_POLICY_NAME_RE = /^[A-Za-z0-9._-]{1,128}$/;
|
|
3448
|
+
});
|
|
3449
|
+
|
|
3450
|
+
// src/hooks/pack-store.ts
|
|
3451
|
+
var MAX_ARTIFACT_BYTES, CORE_SOURCE = "FailproofAI/policies", RETIRED_CORE_ALIASES;
|
|
3452
|
+
var init_pack_store = __esm(() => {
|
|
3453
|
+
init_pack_manifest();
|
|
3454
|
+
MAX_ARTIFACT_BYTES = 8 * 1024 * 1024;
|
|
3455
|
+
RETIRED_CORE_ALIASES = new Set(["core", "failproofai", "official"]);
|
|
3456
|
+
});
|
|
3457
|
+
|
|
3458
|
+
// src/hooks/pack-param-key.ts
|
|
3459
|
+
function packPolicyParamKey(packId, policyName) {
|
|
3460
|
+
return `pack/${packId}/${policyName}`;
|
|
3461
|
+
}
|
|
3462
|
+
function parsePackPolicyName(canonicalName) {
|
|
3463
|
+
if (!canonicalName.startsWith("pack/"))
|
|
3464
|
+
return null;
|
|
3465
|
+
const rest = canonicalName.slice("pack/".length);
|
|
3466
|
+
const slash = rest.lastIndexOf("/");
|
|
3467
|
+
if (slash <= 0)
|
|
3468
|
+
return null;
|
|
3469
|
+
const idWithVersion = rest.slice(0, slash);
|
|
3470
|
+
const at2 = idWithVersion.lastIndexOf("@");
|
|
3471
|
+
return {
|
|
3472
|
+
packId: at2 > 0 ? idWithVersion.slice(0, at2) : idWithVersion,
|
|
3473
|
+
name: rest.slice(slash + 1)
|
|
3474
|
+
};
|
|
3475
|
+
}
|
|
3476
|
+
|
|
3477
|
+
// package.json
|
|
3478
|
+
var version = "1.0.2";
|
|
3479
|
+
var init_package = () => {};
|
|
3480
|
+
|
|
3481
|
+
// src/posthog-key.ts
|
|
3482
|
+
var POSTHOG_API_KEY = "phc_Ac1Ww1GqKc0z1SyrRWbmatEeQdlOQIsDEEdP8l8JRgX", POSTHOG_PRODUCT = "failproofai-oss";
|
|
3483
|
+
|
|
3484
|
+
// src/hooks/fp-config.ts
|
|
3485
|
+
import { chmodSync, existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4, statSync as statSync4, writeFileSync as writeFileSync2 } from "node:fs";
|
|
3486
|
+
function detectLayout() {
|
|
3487
|
+
const raw = readVersionFile();
|
|
3488
|
+
if (raw) {
|
|
3489
|
+
if (raw.layout === LAYOUT_VERSION)
|
|
3490
|
+
return { kind: "current", version: raw };
|
|
3491
|
+
return raw.layout > LAYOUT_VERSION ? { kind: "future", found: raw.layout } : { kind: "stale", found: raw.layout };
|
|
3492
|
+
}
|
|
3493
|
+
if (existsSync5(configFile())) {
|
|
3494
|
+
const layoutThreePositions = [
|
|
3495
|
+
legacy.authJson(),
|
|
3496
|
+
legacy.nextAudit(),
|
|
3497
|
+
legacy.auditSchedule()
|
|
3498
|
+
];
|
|
3499
|
+
if (layoutThreePositions.some((p) => existsSync5(p)))
|
|
3500
|
+
return { kind: "stale", found: 3 };
|
|
3501
|
+
return { kind: "current", version: { layout: LAYOUT_VERSION, cli: "" }, inferred: true };
|
|
3502
|
+
}
|
|
3503
|
+
if (existsSync5(legacy.configToml()))
|
|
3504
|
+
return { kind: "stale", found: 2 };
|
|
3505
|
+
const layoutOneMarkers = [
|
|
3506
|
+
legacy.policyConfig(),
|
|
3507
|
+
legacy.cacheDir(),
|
|
3508
|
+
legacy.launcherMarker(),
|
|
3509
|
+
legacy.lastVersion(),
|
|
3510
|
+
legacy.cloudCredentials(),
|
|
3511
|
+
legacy.ingestCredentials(),
|
|
3512
|
+
legacy.auditDashboard()
|
|
3513
|
+
];
|
|
3514
|
+
return layoutOneMarkers.some((p) => existsSync5(p)) ? { kind: "stale", found: 1 } : { kind: "absent" };
|
|
3515
|
+
}
|
|
3516
|
+
function readLegacyTomlVersion(text) {
|
|
3517
|
+
const layout = Number(/^\s*layout\s*=\s*(\d+)\s*$/m.exec(text)?.[1]);
|
|
3518
|
+
if (!Number.isFinite(layout))
|
|
3519
|
+
return null;
|
|
3520
|
+
const str = (key) => new RegExp(`^\\s*${key}\\s*=\\s*"([^"]*)"`, "m").exec(text)?.[1];
|
|
3521
|
+
return { layout, cli: str("cli") ?? "", daemon: str("daemon") };
|
|
3522
|
+
}
|
|
3523
|
+
function readVersionFile() {
|
|
3524
|
+
let text;
|
|
3525
|
+
try {
|
|
3526
|
+
text = readFileSync4(versionFile(), "utf8");
|
|
3527
|
+
} catch {
|
|
3528
|
+
return null;
|
|
3529
|
+
}
|
|
3530
|
+
try {
|
|
3531
|
+
const parsed = JSON.parse(text);
|
|
3532
|
+
const layout = Number(parsed.layout);
|
|
3533
|
+
if (!Number.isFinite(layout))
|
|
3534
|
+
return null;
|
|
3535
|
+
return {
|
|
3536
|
+
layout,
|
|
3537
|
+
cli: typeof parsed.cli === "string" ? parsed.cli : "",
|
|
3538
|
+
daemon: typeof parsed.daemon === "string" ? parsed.daemon : undefined
|
|
3539
|
+
};
|
|
3540
|
+
} catch {
|
|
3541
|
+
return readLegacyTomlVersion(text);
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3544
|
+
function readIntervalDays(raw) {
|
|
3545
|
+
if (typeof raw !== "number" || !Number.isFinite(raw))
|
|
3546
|
+
return DEFAULT_AUDIT_INTERVAL_DAYS;
|
|
3547
|
+
const days = Math.floor(raw);
|
|
3548
|
+
if (days < MIN_AUDIT_INTERVAL_DAYS)
|
|
3549
|
+
return DEFAULT_AUDIT_INTERVAL_DAYS;
|
|
3550
|
+
return Math.min(days, MAX_AUDIT_INTERVAL_DAYS);
|
|
3551
|
+
}
|
|
3552
|
+
function readSources(raw) {
|
|
3553
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
3554
|
+
return;
|
|
3555
|
+
const out = {};
|
|
3556
|
+
for (const [name, table] of Object.entries(raw)) {
|
|
3557
|
+
if (!table || typeof table !== "object" || Array.isArray(table))
|
|
2642
3558
|
continue;
|
|
2643
3559
|
const entries = table.extra_paths;
|
|
2644
3560
|
if (!Array.isArray(entries))
|
|
@@ -2651,7 +3567,7 @@ function readSources(raw) {
|
|
|
2651
3567
|
}
|
|
2652
3568
|
function readConfig() {
|
|
2653
3569
|
try {
|
|
2654
|
-
const parsed = JSON.parse(
|
|
3570
|
+
const parsed = JSON.parse(readFileSync4(configFile(), "utf8"));
|
|
2655
3571
|
return projectConfig(parsed);
|
|
2656
3572
|
} catch {
|
|
2657
3573
|
return structuredClone(DEFAULT_CONFIG);
|
|
@@ -2871,12 +3787,26 @@ function appendHint(baseReason, hint) {
|
|
|
2871
3787
|
return normalizedHint;
|
|
2872
3788
|
return `${base}. ${normalizedHint}`;
|
|
2873
3789
|
}
|
|
3790
|
+
function readPackPolicyParams(policyParams, packId, policyName) {
|
|
3791
|
+
if (!policyParams)
|
|
3792
|
+
return;
|
|
3793
|
+
const qualified = policyParams[packPolicyParamKey(packId, policyName)];
|
|
3794
|
+
if (qualified)
|
|
3795
|
+
return qualified;
|
|
3796
|
+
if (packId.toLowerCase() === CORE_SOURCE.toLowerCase())
|
|
3797
|
+
return policyParams[policyName];
|
|
3798
|
+
return;
|
|
3799
|
+
}
|
|
2874
3800
|
function getConfigParamsFor(config, canonicalName) {
|
|
2875
3801
|
if (!config?.policyParams)
|
|
2876
3802
|
return;
|
|
2877
3803
|
const canonicalParams = config.policyParams[canonicalName];
|
|
2878
3804
|
if (canonicalParams)
|
|
2879
3805
|
return canonicalParams;
|
|
3806
|
+
const packPolicy = parsePackPolicyName(canonicalName);
|
|
3807
|
+
if (packPolicy) {
|
|
3808
|
+
return readPackPolicyParams(config.policyParams, packPolicy.packId, packPolicy.name);
|
|
3809
|
+
}
|
|
2880
3810
|
const defaultPrefix = `${DEFAULT_POLICY_NAMESPACE}/`;
|
|
2881
3811
|
if (!canonicalName.startsWith(defaultPrefix))
|
|
2882
3812
|
return;
|
|
@@ -3598,6 +4528,7 @@ You MUST complete the above action(s) NOW. Do NOT ask the user for confirmation
|
|
|
3598
4528
|
return { exitCode: 0, stdout: "", stderr: "", policyName: null, reason: null, decision: "allow" };
|
|
3599
4529
|
}
|
|
3600
4530
|
var init_policy_evaluator = __esm(() => {
|
|
4531
|
+
init_pack_store();
|
|
3601
4532
|
init_hook_logger();
|
|
3602
4533
|
init_hook_telemetry();
|
|
3603
4534
|
init_telemetry_id();
|
|
@@ -3629,9 +4560,9 @@ var init_custom_hooks_registry = __esm(() => {
|
|
|
3629
4560
|
// src/hooks/loader-utils.ts
|
|
3630
4561
|
import { readFile as readFile2, writeFile as writeFile2, unlink, access, mkdir, lstat } from "fs/promises";
|
|
3631
4562
|
import { tmpdir } from "os";
|
|
3632
|
-
import { resolve as
|
|
4563
|
+
import { resolve as resolve6, dirname as dirname2, relative as relative2, join as join3 } from "path";
|
|
3633
4564
|
import { pathToFileURL } from "url";
|
|
3634
|
-
import { createHash } from "crypto";
|
|
4565
|
+
import { createHash as createHash3 } from "crypto";
|
|
3635
4566
|
function isTmpArtifact(name) {
|
|
3636
4567
|
return TMP_ARTIFACT_RE.test(name);
|
|
3637
4568
|
}
|
|
@@ -3642,7 +4573,7 @@ async function sweepStaleTmpArtifacts(dir, now = Date.now()) {
|
|
|
3642
4573
|
for (const name of await readdir(dir)) {
|
|
3643
4574
|
if (!isTmpArtifact(name))
|
|
3644
4575
|
continue;
|
|
3645
|
-
const path2 =
|
|
4576
|
+
const path2 = resolve6(dir, name);
|
|
3646
4577
|
try {
|
|
3647
4578
|
const info = await stat(path2);
|
|
3648
4579
|
if (now - info.mtimeMs < TMP_SWEEP_MIN_AGE_MS)
|
|
@@ -3665,14 +4596,14 @@ async function fileExists(path2) {
|
|
|
3665
4596
|
async function findDistIndex() {
|
|
3666
4597
|
const distPath = process.env.FAILPROOFAI_DIST_PATH;
|
|
3667
4598
|
if (distPath) {
|
|
3668
|
-
const candidate =
|
|
4599
|
+
const candidate = resolve6(distPath, "index.js");
|
|
3669
4600
|
if (await fileExists(candidate))
|
|
3670
4601
|
return candidate;
|
|
3671
4602
|
}
|
|
3672
4603
|
const candidates = [
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
4604
|
+
resolve6(dirname2(process.execPath), "..", "assets", "dist", "index.js"),
|
|
4605
|
+
resolve6(process.cwd(), "dist", "index.js"),
|
|
4606
|
+
resolve6(process.cwd(), "node_modules", "failproofai", "dist", "index.js")
|
|
3676
4607
|
];
|
|
3677
4608
|
for (const c of candidates) {
|
|
3678
4609
|
if (await fileExists(c))
|
|
@@ -3681,8 +4612,8 @@ async function findDistIndex() {
|
|
|
3681
4612
|
return null;
|
|
3682
4613
|
}
|
|
3683
4614
|
async function resolveLocalImport(fromDir, specifier) {
|
|
3684
|
-
const base =
|
|
3685
|
-
const candidates = [base, `${base}.js`, `${base}.mjs`, `${base}.ts`,
|
|
4615
|
+
const base = resolve6(fromDir, specifier);
|
|
4616
|
+
const candidates = [base, `${base}.js`, `${base}.mjs`, `${base}.ts`, resolve6(base, "index.js")];
|
|
3686
4617
|
for (const c of candidates) {
|
|
3687
4618
|
if (await fileExists(c))
|
|
3688
4619
|
return c;
|
|
@@ -3745,7 +4676,7 @@ async function rewriteFileTree(entryPath, distUrl, distIndex, tmpSuffix = TMP_SU
|
|
|
3745
4676
|
let code;
|
|
3746
4677
|
if (entrySha256 && filePath === entryPath) {
|
|
3747
4678
|
const buf = await readFile2(filePath);
|
|
3748
|
-
const actual =
|
|
4679
|
+
const actual = createHash3("sha256").update(buf).digest("hex");
|
|
3749
4680
|
if (actual !== entrySha256) {
|
|
3750
4681
|
throw new Error(`cloud-managed policy failed integrity re-verification at load: expected ${entrySha256}, got ${actual}`);
|
|
3751
4682
|
}
|
|
@@ -3774,7 +4705,7 @@ async function rewriteFileTree(entryPath, distUrl, distIndex, tmpSuffix = TMP_SU
|
|
|
3774
4705
|
if (!visited.has(resolved2) && !queue.includes(resolved2)) {
|
|
3775
4706
|
queue.push(resolved2);
|
|
3776
4707
|
}
|
|
3777
|
-
let relPath =
|
|
4708
|
+
let relPath = relative2(dir, resolved2 + tmpSuffix).split("\\").join("/");
|
|
3778
4709
|
if (!relPath.startsWith("."))
|
|
3779
4710
|
relPath = "./" + relPath;
|
|
3780
4711
|
rewrites.set(specifier, relPath);
|
|
@@ -3810,24 +4741,24 @@ var init_loader_utils = __esm(() => {
|
|
|
3810
4741
|
});
|
|
3811
4742
|
|
|
3812
4743
|
// src/hooks/custom-hooks-loader.ts
|
|
3813
|
-
import { resolve as
|
|
4744
|
+
import { resolve as resolve7, isAbsolute as isAbsolute2, basename } from "node:path";
|
|
3814
4745
|
import { randomUUID } from "crypto";
|
|
3815
|
-
import { existsSync as
|
|
4746
|
+
import { existsSync as existsSync6, readdirSync } from "node:fs";
|
|
3816
4747
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
3817
4748
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
3818
|
-
import { createHash as
|
|
4749
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
3819
4750
|
function discoverPolicyFiles(dir) {
|
|
3820
|
-
if (!
|
|
4751
|
+
if (!existsSync6(dir))
|
|
3821
4752
|
return [];
|
|
3822
4753
|
try {
|
|
3823
4754
|
const entries = readdirSync(dir, { withFileTypes: true });
|
|
3824
|
-
return entries.filter((e) => e.isFile() && CONVENTION_FILE_RE.test(e.name)).sort((a, b) => a.name.localeCompare(b.name)).map((e) =>
|
|
4755
|
+
return entries.filter((e) => e.isFile() && CONVENTION_FILE_RE.test(e.name)).sort((a, b) => a.name.localeCompare(b.name)).map((e) => resolve7(dir, e.name));
|
|
3825
4756
|
} catch {
|
|
3826
4757
|
return [];
|
|
3827
4758
|
}
|
|
3828
4759
|
}
|
|
3829
4760
|
function findSkippedPolicyFiles(dir) {
|
|
3830
|
-
if (!
|
|
4761
|
+
if (!existsSync6(dir))
|
|
3831
4762
|
return [];
|
|
3832
4763
|
try {
|
|
3833
4764
|
return readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && LOADABLE_EXT_RE.test(e.name) && !CONVENTION_FILE_RE.test(e.name) && !e.name.endsWith(".d.ts") && !isTmpArtifact(e.name)).map((e) => e.name).sort((a, b) => a.localeCompare(b));
|
|
@@ -3902,7 +4833,7 @@ async function loadSingleFile(absPath, opts) {
|
|
|
3902
4833
|
}
|
|
3903
4834
|
}
|
|
3904
4835
|
async function fingerprintTemporaryTree(tmpFiles, tmpSuffix) {
|
|
3905
|
-
const hash =
|
|
4836
|
+
const hash = createHash4("sha256");
|
|
3906
4837
|
for (const tmpFile of [...tmpFiles].sort()) {
|
|
3907
4838
|
hash.update(tmpFile.replaceAll(tmpSuffix, TMP_SUFFIX));
|
|
3908
4839
|
const contents = await readFile3(tmpFile, "utf8");
|
|
@@ -3946,7 +4877,7 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
|
|
|
3946
4877
|
const loadedPaths = new Set;
|
|
3947
4878
|
const cloudManagedByPath = new Map;
|
|
3948
4879
|
for (const policy of opts?.cloudManagedPolicies ?? []) {
|
|
3949
|
-
const key =
|
|
4880
|
+
const key = resolve7(policy.path);
|
|
3950
4881
|
const existing = cloudManagedByPath.get(key);
|
|
3951
4882
|
if (!existing) {
|
|
3952
4883
|
cloudManagedByPath.set(key, policy);
|
|
@@ -3957,32 +4888,46 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
|
|
|
3957
4888
|
cloudManagedByPath.set(key, policy);
|
|
3958
4889
|
}
|
|
3959
4890
|
}
|
|
4891
|
+
const unionSelection = (a, b) => a === null || b === null ? null : [...new Set([...a, ...b])];
|
|
4892
|
+
const unionCatalog = (winner, other) => [
|
|
4893
|
+
...winner,
|
|
4894
|
+
...other.filter((p) => !winner.some((w) => w.name === p.name))
|
|
4895
|
+
];
|
|
3960
4896
|
const packByPath = new Map;
|
|
4897
|
+
const packIdsByPath = new Map;
|
|
3961
4898
|
for (const pack of opts?.packs ?? []) {
|
|
3962
|
-
const key =
|
|
4899
|
+
const key = resolve7(pack.path);
|
|
4900
|
+
packIdsByPath.set(key, [...packIdsByPath.get(key) ?? [], pack.id]);
|
|
3963
4901
|
const existing = packByPath.get(key);
|
|
3964
4902
|
if (!existing) {
|
|
3965
4903
|
packByPath.set(key, pack);
|
|
3966
4904
|
continue;
|
|
3967
4905
|
}
|
|
3968
|
-
hookLogWarn(`packs ${existing.id} and ${pack.id} have identical source, so they share one artifact ` + `and load as one pack; enforcing it if either asks to enforce`);
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
4906
|
+
hookLogWarn(`packs ${existing.id} and ${pack.id} have identical source, so they share one artifact ` + `and load as one pack; enforcing it if either asks to enforce, and taking ` + `the union of what each has enabled and of the agents each guards`);
|
|
4907
|
+
const winner = existing.effect !== "enforce" && pack.effect === "enforce" ? pack : existing;
|
|
4908
|
+
const other = winner === existing ? pack : existing;
|
|
4909
|
+
packByPath.set(key, {
|
|
4910
|
+
...winner,
|
|
4911
|
+
policies: unionCatalog(winner.policies, other.policies),
|
|
4912
|
+
enabled: unionSelection(winner.enabled, other.enabled),
|
|
4913
|
+
clis: unionSelection(winner.clis, other.clis)
|
|
4914
|
+
});
|
|
3972
4915
|
}
|
|
3973
4916
|
for (const customPoliciesPath of typeof customPoliciesPaths === "string" ? [customPoliciesPaths] : customPoliciesPaths ?? []) {
|
|
3974
|
-
const absPath =
|
|
4917
|
+
const absPath = resolve7(projectRoot, customPoliciesPath);
|
|
3975
4918
|
const cloudManaged = cloudManagedByPath.get(absPath);
|
|
3976
4919
|
const pack = packByPath.get(absPath);
|
|
3977
|
-
if (
|
|
4920
|
+
if (existsSync6(absPath)) {
|
|
3978
4921
|
if (!loadedPaths.has(absPath)) {
|
|
3979
4922
|
loadedPaths.add(absPath);
|
|
3980
4923
|
const hooksBefore = getCustomHooks().length;
|
|
3981
4924
|
const failure = await loadSingleFile(absPath, {
|
|
3982
4925
|
verifyEntrySha: cloudManaged?.sha256 ?? pack?.sha256
|
|
3983
4926
|
});
|
|
3984
|
-
if (failure && pack)
|
|
3985
|
-
|
|
4927
|
+
if (failure && pack) {
|
|
4928
|
+
for (const id of packIdsByPath.get(absPath) ?? [pack.id])
|
|
4929
|
+
packFailures.set(id, failure);
|
|
4930
|
+
}
|
|
3986
4931
|
for (const hook of getCustomHooks().slice(hooksBefore)) {
|
|
3987
4932
|
const tagged = hook;
|
|
3988
4933
|
if (cloudManaged) {
|
|
@@ -3999,12 +4944,15 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
|
|
|
3999
4944
|
}
|
|
4000
4945
|
} else {
|
|
4001
4946
|
hookLogWarn(`custom policy path not found: ${absPath}`);
|
|
4002
|
-
if (pack)
|
|
4003
|
-
|
|
4947
|
+
if (pack) {
|
|
4948
|
+
const missing = { type: "path_missing", reason: `path missing: ${absPath}` };
|
|
4949
|
+
for (const id of packIdsByPath.get(absPath) ?? [pack.id])
|
|
4950
|
+
packFailures.set(id, missing);
|
|
4951
|
+
}
|
|
4004
4952
|
}
|
|
4005
4953
|
}
|
|
4006
4954
|
const hooksBeforeConvention = getCustomHooks().length;
|
|
4007
|
-
const projectDir =
|
|
4955
|
+
const projectDir = resolve7(projectRoot, ".failproofai", "policies");
|
|
4008
4956
|
const projectScope = projectDir === customPoliciesDir() ? "user" : "project";
|
|
4009
4957
|
if (conventionEnabled) {
|
|
4010
4958
|
sweepStaleTmpArtifacts(projectDir);
|
|
@@ -4072,7 +5020,13 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
|
|
|
4072
5020
|
hook.__conventionScope = hookNameToScope.get(hook.name) ?? "project";
|
|
4073
5021
|
}
|
|
4074
5022
|
}
|
|
4075
|
-
|
|
5023
|
+
const packAliases = new Map;
|
|
5024
|
+
for (const [path2, ids] of packIdsByPath) {
|
|
5025
|
+
const winner = packByPath.get(path2);
|
|
5026
|
+
if (winner && ids.length > 1)
|
|
5027
|
+
packAliases.set(winner.id, ids);
|
|
5028
|
+
}
|
|
5029
|
+
return { hooks: allHooks, conventionSources, packFailures, packAliases };
|
|
4076
5030
|
}
|
|
4077
5031
|
var LOADING_KEY = "__FAILPROOFAI_LOADING_HOOKS__", CONVENTION_FILE_RE, loadSequence = 0, policyModuleCache, POLICY_MODULE_CACHE_MAX_ENTRIES = 500, LOADABLE_EXT_RE;
|
|
4078
5032
|
var init_custom_hooks_loader = __esm(() => {
|
|
@@ -4090,19 +5044,19 @@ var init_custom_hooks_loader = __esm(() => {
|
|
|
4090
5044
|
|
|
4091
5045
|
// src/hooks/hook-activity-store.ts
|
|
4092
5046
|
import {
|
|
4093
|
-
readFileSync as
|
|
5047
|
+
readFileSync as readFileSync5,
|
|
4094
5048
|
writeFileSync as writeFileSync3,
|
|
4095
5049
|
appendFileSync as appendFileSync2,
|
|
4096
5050
|
renameSync as renameSync2,
|
|
4097
5051
|
readdirSync as readdirSync2,
|
|
4098
5052
|
mkdirSync as mkdirSync4,
|
|
4099
|
-
existsSync as
|
|
5053
|
+
existsSync as existsSync7,
|
|
4100
5054
|
statSync as statSync5,
|
|
4101
5055
|
unlinkSync
|
|
4102
5056
|
} from "node:fs";
|
|
4103
5057
|
import { join as join4 } from "node:path";
|
|
4104
5058
|
function ensureDir() {
|
|
4105
|
-
if (!
|
|
5059
|
+
if (!existsSync7(storeDirValue())) {
|
|
4106
5060
|
mkdirSync4(storeDirValue(), { recursive: true });
|
|
4107
5061
|
}
|
|
4108
5062
|
}
|
|
@@ -4163,7 +5117,7 @@ function rotate(currentPath, countPath) {
|
|
|
4163
5117
|
}
|
|
4164
5118
|
function readCount(countPath) {
|
|
4165
5119
|
try {
|
|
4166
|
-
const n = parseInt(
|
|
5120
|
+
const n = parseInt(readFileSync5(countPath, "utf-8"), 10);
|
|
4167
5121
|
return isNaN(n) ? 0 : n;
|
|
4168
5122
|
} catch {
|
|
4169
5123
|
return 0;
|
|
@@ -4176,7 +5130,7 @@ function writeCount(countPath, n) {
|
|
|
4176
5130
|
}
|
|
4177
5131
|
function readStoredStats() {
|
|
4178
5132
|
try {
|
|
4179
|
-
return JSON.parse(
|
|
5133
|
+
return JSON.parse(readFileSync5(join4(storeDirValue(), STATS_FILE), "utf-8"));
|
|
4180
5134
|
} catch {
|
|
4181
5135
|
return { totalEvents: 0, denyCount: 0, policyMap: {} };
|
|
4182
5136
|
}
|
|
@@ -4927,7 +5881,7 @@ var init_cursor_projects = __esm(() => {
|
|
|
4927
5881
|
});
|
|
4928
5882
|
|
|
4929
5883
|
// lib/sqlite-reader.ts
|
|
4930
|
-
import { existsSync as
|
|
5884
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "node:fs";
|
|
4931
5885
|
import initSqlJs from "sql.js/dist/sql-asm.js";
|
|
4932
5886
|
async function tryNodeSqlite(dbPath) {
|
|
4933
5887
|
try {
|
|
@@ -4954,7 +5908,7 @@ function loadSqlJs() {
|
|
|
4954
5908
|
async function trySqlJs(dbPath) {
|
|
4955
5909
|
try {
|
|
4956
5910
|
const SQL = await loadSqlJs();
|
|
4957
|
-
const db = new SQL.Database(
|
|
5911
|
+
const db = new SQL.Database(readFileSync6(dbPath));
|
|
4958
5912
|
return {
|
|
4959
5913
|
query(sql, params) {
|
|
4960
5914
|
const stmt = db.prepare(sql);
|
|
@@ -4978,7 +5932,7 @@ async function trySqlJs(dbPath) {
|
|
|
4978
5932
|
}
|
|
4979
5933
|
}
|
|
4980
5934
|
async function openSqliteReadonly(dbPath) {
|
|
4981
|
-
if (!
|
|
5935
|
+
if (!existsSync8(dbPath))
|
|
4982
5936
|
return null;
|
|
4983
5937
|
return await tryNodeSqlite(dbPath) ?? await trySqlJs(dbPath);
|
|
4984
5938
|
}
|
|
@@ -5325,11 +6279,11 @@ var init_pi_projects = __esm(() => {
|
|
|
5325
6279
|
// lib/hermes-profiles.ts
|
|
5326
6280
|
import { readdirSync as readdirSync3 } from "node:fs";
|
|
5327
6281
|
import { homedir as homedir10 } from "node:os";
|
|
5328
|
-
import { basename as basename2, dirname as dirname3, join as join11, resolve as
|
|
6282
|
+
import { basename as basename2, dirname as dirname3, join as join11, resolve as resolve8 } from "node:path";
|
|
5329
6283
|
function hermesRoot() {
|
|
5330
6284
|
const env = (process.env.HERMES_HOME || "").trim();
|
|
5331
6285
|
if (env) {
|
|
5332
|
-
const home =
|
|
6286
|
+
const home = resolve8(env);
|
|
5333
6287
|
const parent = dirname3(home);
|
|
5334
6288
|
if (basename2(parent) === PROFILES_DIR)
|
|
5335
6289
|
return dirname3(parent);
|
|
@@ -5913,7 +6867,7 @@ __export(exports_openclaw_projects, {
|
|
|
5913
6867
|
openClawProjectPath: () => openClawProjectPath,
|
|
5914
6868
|
parseOpenClawProjectName: () => parseOpenClawProjectName
|
|
5915
6869
|
});
|
|
5916
|
-
import { readFileSync as
|
|
6870
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
5917
6871
|
import { join as join14 } from "node:path";
|
|
5918
6872
|
function str(v) {
|
|
5919
6873
|
return typeof v === "string" && v.length > 0 ? v : undefined;
|
|
@@ -5923,7 +6877,7 @@ function readSessionsIndex(agentId) {
|
|
|
5923
6877
|
const indexPath = join14(openclawHome(), "agents", agentId, "sessions", "sessions.json");
|
|
5924
6878
|
let raw;
|
|
5925
6879
|
try {
|
|
5926
|
-
raw = JSON.parse(
|
|
6880
|
+
raw = JSON.parse(readFileSync7(indexPath, "utf-8"));
|
|
5927
6881
|
} catch {
|
|
5928
6882
|
return out;
|
|
5929
6883
|
}
|
|
@@ -7278,7 +8232,7 @@ var init_goose_projects = __esm(() => {
|
|
|
7278
8232
|
|
|
7279
8233
|
// lib/projects.ts
|
|
7280
8234
|
import { readdir as readdir5, stat as stat4 } from "node:fs/promises";
|
|
7281
|
-
import { join as join20, resolve as
|
|
8235
|
+
import { join as join20, resolve as resolve9, sep } from "node:path";
|
|
7282
8236
|
async function getMtime(path2, label) {
|
|
7283
8237
|
try {
|
|
7284
8238
|
return (await stat4(path2)).mtime;
|
|
@@ -7430,8 +8384,8 @@ function resolveProjectPath(projectName) {
|
|
|
7430
8384
|
throw new RangeError("Empty project name");
|
|
7431
8385
|
if (/^[/\\]/.test(projectName))
|
|
7432
8386
|
throw new RangeError("Absolute project name");
|
|
7433
|
-
const projectsPath =
|
|
7434
|
-
const candidate =
|
|
8387
|
+
const projectsPath = resolve9(getClaudeProjectsPath());
|
|
8388
|
+
const candidate = resolve9(join20(projectsPath, projectName));
|
|
7435
8389
|
if (!candidate.startsWith(projectsPath + sep)) {
|
|
7436
8390
|
throw new RangeError("Project path escapes root");
|
|
7437
8391
|
}
|
|
@@ -7478,7 +8432,7 @@ var init_projects = __esm(() => {
|
|
|
7478
8432
|
|
|
7479
8433
|
// lib/resolve-subagent-path.ts
|
|
7480
8434
|
import { access as access2 } from "fs/promises";
|
|
7481
|
-
import { join as join21, relative as
|
|
8435
|
+
import { join as join21, relative as relative3 } from "path";
|
|
7482
8436
|
async function resolveSubagentPath(projectsPath, projectName, sessionId, agentId) {
|
|
7483
8437
|
const fileName = `agent-${agentId}.jsonl`;
|
|
7484
8438
|
const candidatePaths = [
|
|
@@ -7487,7 +8441,7 @@ async function resolveSubagentPath(projectsPath, projectName, sessionId, agentId
|
|
|
7487
8441
|
join21(projectsPath, projectName, sessionId, "subagents", fileName)
|
|
7488
8442
|
];
|
|
7489
8443
|
for (const candidatePath of candidatePaths) {
|
|
7490
|
-
if (
|
|
8444
|
+
if (relative3(projectsPath, candidatePath).startsWith("..")) {
|
|
7491
8445
|
continue;
|
|
7492
8446
|
}
|
|
7493
8447
|
try {
|
|
@@ -7737,15 +8691,15 @@ var init_log_entries = __esm(() => {
|
|
|
7737
8691
|
});
|
|
7738
8692
|
|
|
7739
8693
|
// lib/codex-sessions.ts
|
|
7740
|
-
import { readFileSync as
|
|
8694
|
+
import { readFileSync as readFileSync8, readdirSync as readdirSync7, existsSync as existsSync9, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, statSync as statSync9 } from "node:fs";
|
|
7741
8695
|
import { readFile as readFile11 } from "node:fs/promises";
|
|
7742
8696
|
import { dirname as dirname4, join as join23 } from "node:path";
|
|
7743
8697
|
import { homedir as homedir16 } from "node:os";
|
|
7744
8698
|
function readCache() {
|
|
7745
8699
|
try {
|
|
7746
|
-
if (!
|
|
8700
|
+
if (!existsSync9(CACHE_PATH))
|
|
7747
8701
|
return {};
|
|
7748
|
-
return JSON.parse(
|
|
8702
|
+
return JSON.parse(readFileSync8(CACHE_PATH, "utf-8"));
|
|
7749
8703
|
} catch {
|
|
7750
8704
|
return {};
|
|
7751
8705
|
}
|
|
@@ -7771,7 +8725,7 @@ function dirSearch(dir, sessionId) {
|
|
|
7771
8725
|
function findCodexTranscript(sessionId) {
|
|
7772
8726
|
const cache = readCache();
|
|
7773
8727
|
const cached = cache[sessionId];
|
|
7774
|
-
if (cached &&
|
|
8728
|
+
if (cached && existsSync9(cached))
|
|
7775
8729
|
return cached;
|
|
7776
8730
|
const root = join23(homedir16(), ".codex", "sessions");
|
|
7777
8731
|
const today = new Date;
|
|
@@ -8025,7 +8979,7 @@ var init_codex_sessions = __esm(() => {
|
|
|
8025
8979
|
});
|
|
8026
8980
|
|
|
8027
8981
|
// src/hooks/resolve-permission-mode.ts
|
|
8028
|
-
import { readFileSync as
|
|
8982
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
8029
8983
|
function resolvePermissionMode(integration, parsed, sessionId) {
|
|
8030
8984
|
if (integration === "claude") {
|
|
8031
8985
|
return parsed.permission_mode ?? "default";
|
|
@@ -8040,7 +8994,7 @@ function resolveCodexMode(sessionId) {
|
|
|
8040
8994
|
const path2 = findCodexTranscript(sessionId);
|
|
8041
8995
|
if (!path2)
|
|
8042
8996
|
return;
|
|
8043
|
-
for (const line of
|
|
8997
|
+
for (const line of readFileSync9(path2, "utf-8").split(`
|
|
8044
8998
|
`)) {
|
|
8045
8999
|
if (!line.includes("turn_context"))
|
|
8046
9000
|
continue;
|
|
@@ -8065,9 +9019,9 @@ var init_resolve_permission_mode = __esm(() => {
|
|
|
8065
9019
|
});
|
|
8066
9020
|
|
|
8067
9021
|
// lib/copilot-sessions.ts
|
|
8068
|
-
import { readFileSync as
|
|
9022
|
+
import { readFileSync as readFileSync10, readdirSync as readdirSync8, existsSync as existsSync10, statSync as statSync10 } from "node:fs";
|
|
8069
9023
|
import { readFile as readFile12 } from "node:fs/promises";
|
|
8070
|
-
import { join as join24, resolve as
|
|
9024
|
+
import { join as join24, resolve as resolve10, sep as sep2 } from "node:path";
|
|
8071
9025
|
import { homedir as homedir17 } from "node:os";
|
|
8072
9026
|
function getCopilotHome() {
|
|
8073
9027
|
return process.env.COPILOT_HOME || join24(homedir17(), ".copilot");
|
|
@@ -8078,8 +9032,8 @@ function getCopilotSessionStateRoot2() {
|
|
|
8078
9032
|
function getCopilotSessionDir(sessionId) {
|
|
8079
9033
|
if (!sessionId)
|
|
8080
9034
|
return null;
|
|
8081
|
-
const root =
|
|
8082
|
-
const candidate =
|
|
9035
|
+
const root = resolve10(getCopilotSessionStateRoot2());
|
|
9036
|
+
const candidate = resolve10(root, sessionId);
|
|
8083
9037
|
if (candidate === root || !candidate.startsWith(`${root}${sep2}`))
|
|
8084
9038
|
return null;
|
|
8085
9039
|
return candidate;
|
|
@@ -8094,17 +9048,17 @@ function findCopilotTranscript(sessionId) {
|
|
|
8094
9048
|
const candidate = resolveSessionFile(sessionId, "events.jsonl");
|
|
8095
9049
|
if (!candidate)
|
|
8096
9050
|
return null;
|
|
8097
|
-
return
|
|
9051
|
+
return existsSync10(candidate) ? candidate : null;
|
|
8098
9052
|
}
|
|
8099
9053
|
function findCopilotWorkspace(sessionId) {
|
|
8100
9054
|
const candidate = resolveSessionFile(sessionId, "workspace.yaml");
|
|
8101
9055
|
if (!candidate)
|
|
8102
9056
|
return null;
|
|
8103
|
-
return
|
|
9057
|
+
return existsSync10(candidate) ? candidate : null;
|
|
8104
9058
|
}
|
|
8105
9059
|
function readYamlScalar(path2, key) {
|
|
8106
9060
|
try {
|
|
8107
|
-
const text =
|
|
9061
|
+
const text = readFileSync10(path2, "utf-8");
|
|
8108
9062
|
const re = new RegExp(`^${key}\\s*:\\s*(.+?)\\s*$`, "m");
|
|
8109
9063
|
const m = text.match(re);
|
|
8110
9064
|
if (!m)
|
|
@@ -8282,9 +9236,9 @@ var init_copilot_sessions = __esm(() => {
|
|
|
8282
9236
|
});
|
|
8283
9237
|
|
|
8284
9238
|
// lib/cursor-sessions.ts
|
|
8285
|
-
import { readFileSync as
|
|
9239
|
+
import { readFileSync as readFileSync11, readdirSync as readdirSync9, existsSync as existsSync11, statSync as statSync11 } from "node:fs";
|
|
8286
9240
|
import { readFile as readFile13 } from "node:fs/promises";
|
|
8287
|
-
import { basename as basename3, join as join25, resolve as
|
|
9241
|
+
import { basename as basename3, join as join25, resolve as resolve11, sep as sep3 } from "node:path";
|
|
8288
9242
|
import { homedir as homedir18 } from "node:os";
|
|
8289
9243
|
function getCursorHome2() {
|
|
8290
9244
|
return process.env.CURSOR_HOME || join25(homedir18(), ".cursor");
|
|
@@ -8292,8 +9246,8 @@ function getCursorHome2() {
|
|
|
8292
9246
|
function getCursorSessionDir(sessionId) {
|
|
8293
9247
|
if (!sessionId)
|
|
8294
9248
|
return null;
|
|
8295
|
-
const home =
|
|
8296
|
-
const projectsRoot =
|
|
9249
|
+
const home = resolve11(getCursorHome2());
|
|
9250
|
+
const projectsRoot = resolve11(home, NEW_PROJECTS_DIR2);
|
|
8297
9251
|
let projectEntries = [];
|
|
8298
9252
|
try {
|
|
8299
9253
|
projectEntries = readdirSync9(projectsRoot, { withFileTypes: true });
|
|
@@ -8301,19 +9255,19 @@ function getCursorSessionDir(sessionId) {
|
|
|
8301
9255
|
for (const entry of projectEntries) {
|
|
8302
9256
|
if (!entry.isDirectory())
|
|
8303
9257
|
continue;
|
|
8304
|
-
const candidate =
|
|
8305
|
-
const transcriptRoot =
|
|
9258
|
+
const candidate = resolve11(projectsRoot, entry.name, NEW_AGENT_TRANSCRIPTS_DIR2, sessionId);
|
|
9259
|
+
const transcriptRoot = resolve11(projectsRoot, entry.name, NEW_AGENT_TRANSCRIPTS_DIR2);
|
|
8306
9260
|
if (candidate === transcriptRoot || !candidate.startsWith(`${transcriptRoot}${sep3}`))
|
|
8307
9261
|
continue;
|
|
8308
|
-
if (
|
|
9262
|
+
if (existsSync11(candidate))
|
|
8309
9263
|
return candidate;
|
|
8310
9264
|
}
|
|
8311
9265
|
for (const sub of LEGACY_SESSION_ROOT_CANDIDATES2) {
|
|
8312
|
-
const root =
|
|
8313
|
-
const candidate =
|
|
9266
|
+
const root = resolve11(home, sub);
|
|
9267
|
+
const candidate = resolve11(root, sessionId);
|
|
8314
9268
|
if (candidate === root || !candidate.startsWith(`${root}${sep3}`))
|
|
8315
9269
|
continue;
|
|
8316
|
-
if (
|
|
9270
|
+
if (existsSync11(candidate))
|
|
8317
9271
|
return candidate;
|
|
8318
9272
|
}
|
|
8319
9273
|
return null;
|
|
@@ -8323,11 +9277,11 @@ function findCursorTranscript(sessionId) {
|
|
|
8323
9277
|
if (!dir)
|
|
8324
9278
|
return null;
|
|
8325
9279
|
const newCandidate = join25(dir, `${basename3(dir)}.jsonl`);
|
|
8326
|
-
if (
|
|
9280
|
+
if (existsSync11(newCandidate))
|
|
8327
9281
|
return newCandidate;
|
|
8328
9282
|
for (const name of LEGACY_TRANSCRIPT_FILE_CANDIDATES2) {
|
|
8329
9283
|
const candidate = join25(dir, name);
|
|
8330
|
-
if (
|
|
9284
|
+
if (existsSync11(candidate))
|
|
8331
9285
|
return candidate;
|
|
8332
9286
|
}
|
|
8333
9287
|
return null;
|
|
@@ -8538,9 +9492,9 @@ var init_cursor_sessions = __esm(() => {
|
|
|
8538
9492
|
});
|
|
8539
9493
|
|
|
8540
9494
|
// lib/pi-sessions.ts
|
|
8541
|
-
import { readFileSync as
|
|
9495
|
+
import { readFileSync as readFileSync12, readdirSync as readdirSync10, existsSync as existsSync12, statSync as statSync12 } from "node:fs";
|
|
8542
9496
|
import { readFile as readFile14 } from "node:fs/promises";
|
|
8543
|
-
import { join as join26, resolve as
|
|
9497
|
+
import { join as join26, resolve as resolve12, sep as sep4 } from "node:path";
|
|
8544
9498
|
import { homedir as homedir19 } from "node:os";
|
|
8545
9499
|
function getPiSessionStateRoot() {
|
|
8546
9500
|
return process.env.PI_SESSIONS_DIR || join26(homedir19(), ".pi", "agent", "sessions");
|
|
@@ -8551,7 +9505,7 @@ function isSafeSessionId(sessionId) {
|
|
|
8551
9505
|
function findPiTranscript(sessionId) {
|
|
8552
9506
|
if (!isSafeSessionId(sessionId))
|
|
8553
9507
|
return null;
|
|
8554
|
-
const root =
|
|
9508
|
+
const root = resolve12(getPiSessionStateRoot());
|
|
8555
9509
|
let cwdDirs;
|
|
8556
9510
|
try {
|
|
8557
9511
|
cwdDirs = readdirSync10(root);
|
|
@@ -8559,7 +9513,7 @@ function findPiTranscript(sessionId) {
|
|
|
8559
9513
|
return null;
|
|
8560
9514
|
}
|
|
8561
9515
|
for (const cwdDir of cwdDirs) {
|
|
8562
|
-
const cwdPath =
|
|
9516
|
+
const cwdPath = resolve12(root, cwdDir);
|
|
8563
9517
|
if (!cwdPath.startsWith(`${root}${sep4}`))
|
|
8564
9518
|
continue;
|
|
8565
9519
|
let files;
|
|
@@ -8572,10 +9526,10 @@ function findPiTranscript(sessionId) {
|
|
|
8572
9526
|
const m = SESSION_FILE_RE2.exec(f);
|
|
8573
9527
|
if (!m || m[1].toLowerCase() !== sessionId.toLowerCase())
|
|
8574
9528
|
continue;
|
|
8575
|
-
const candidate =
|
|
9529
|
+
const candidate = resolve12(cwdPath, f);
|
|
8576
9530
|
if (!candidate.startsWith(`${cwdPath}${sep4}`))
|
|
8577
9531
|
continue;
|
|
8578
|
-
if (
|
|
9532
|
+
if (existsSync12(candidate))
|
|
8579
9533
|
return candidate;
|
|
8580
9534
|
}
|
|
8581
9535
|
}
|
|
@@ -8709,404 +9663,123 @@ async function parsePiLog(fileContent, source = "session") {
|
|
|
8709
9663
|
entries.push({
|
|
8710
9664
|
type: "system",
|
|
8711
9665
|
...baseEntry(rawCopy, timestamp, date, source),
|
|
8712
|
-
raw: rawCopy
|
|
8713
|
-
});
|
|
8714
|
-
continue;
|
|
8715
|
-
}
|
|
8716
|
-
entries.push({
|
|
8717
|
-
type: "system",
|
|
8718
|
-
...baseEntry(rawCopy, timestamp, date, source),
|
|
8719
|
-
raw: rawCopy
|
|
8720
|
-
});
|
|
8721
|
-
}
|
|
8722
|
-
if (entries.length > 500)
|
|
8723
|
-
await new Promise((r) => setImmediate(r));
|
|
8724
|
-
entries.sort((a, b) => a.timestampMs - b.timestampMs);
|
|
8725
|
-
return { entries, rawLines, cwd };
|
|
8726
|
-
}
|
|
8727
|
-
async function getPiSessionLog(sessionId) {
|
|
8728
|
-
const filePath = findPiTranscript(sessionId);
|
|
8729
|
-
if (!filePath)
|
|
8730
|
-
return null;
|
|
8731
|
-
let fileContent;
|
|
8732
|
-
try {
|
|
8733
|
-
fileContent = await readFile14(filePath, "utf-8");
|
|
8734
|
-
} catch {
|
|
8735
|
-
return null;
|
|
8736
|
-
}
|
|
8737
|
-
let parsed;
|
|
8738
|
-
try {
|
|
8739
|
-
parsed = await parsePiLog(fileContent, "session");
|
|
8740
|
-
} catch {
|
|
8741
|
-
return null;
|
|
8742
|
-
}
|
|
8743
|
-
return {
|
|
8744
|
-
entries: parsed.entries,
|
|
8745
|
-
rawLines: parsed.rawLines,
|
|
8746
|
-
cwd: parsed.cwd,
|
|
8747
|
-
filePath
|
|
8748
|
-
};
|
|
8749
|
-
}
|
|
8750
|
-
var UUID_RE, SESSION_FILE_RE2, getCachedPiSessionLog;
|
|
8751
|
-
var init_pi_sessions = __esm(() => {
|
|
8752
|
-
init_log_entries();
|
|
8753
|
-
UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
8754
|
-
SESSION_FILE_RE2 = /^[\d-]+T[\d-]+Z_([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i;
|
|
8755
|
-
getCachedPiSessionLog = runtimeCache((sessionId) => getPiSessionLog(sessionId), 60, { maxSize: 50 });
|
|
8756
|
-
});
|
|
8757
|
-
|
|
8758
|
-
// src/hooks/resolve-transcript-path.ts
|
|
8759
|
-
function resolveTranscriptPath(integration, parsed, sessionId) {
|
|
8760
|
-
const stdinPath = typeof parsed.transcript_path === "string" ? parsed.transcript_path : undefined;
|
|
8761
|
-
if (stdinPath)
|
|
8762
|
-
return stdinPath;
|
|
8763
|
-
if (typeof sessionId !== "string" || sessionId.length === 0)
|
|
8764
|
-
return;
|
|
8765
|
-
switch (integration) {
|
|
8766
|
-
case "claude":
|
|
8767
|
-
return;
|
|
8768
|
-
case "codex":
|
|
8769
|
-
return findCodexTranscript(sessionId) ?? undefined;
|
|
8770
|
-
case "copilot":
|
|
8771
|
-
return findCopilotTranscript(sessionId) ?? undefined;
|
|
8772
|
-
case "cursor":
|
|
8773
|
-
return findCursorTranscript(sessionId) ?? undefined;
|
|
8774
|
-
case "pi":
|
|
8775
|
-
return findPiTranscript(sessionId) ?? undefined;
|
|
8776
|
-
case "factory":
|
|
8777
|
-
return findFactoryTranscript(sessionId) ?? undefined;
|
|
8778
|
-
case "antigravity":
|
|
8779
|
-
return findAntigravityTranscript(sessionId) ?? undefined;
|
|
8780
|
-
case "devin":
|
|
8781
|
-
return `devin-db://${sessionId}`;
|
|
8782
|
-
case "goose":
|
|
8783
|
-
return `goose-db://${sessionId}`;
|
|
8784
|
-
case "opencode":
|
|
8785
|
-
return `opencode-db://${sessionId}`;
|
|
8786
|
-
case "hermes":
|
|
8787
|
-
return;
|
|
8788
|
-
case "openclaw":
|
|
8789
|
-
return;
|
|
8790
|
-
default:
|
|
8791
|
-
return;
|
|
8792
|
-
}
|
|
8793
|
-
}
|
|
8794
|
-
var init_resolve_transcript_path = __esm(() => {
|
|
8795
|
-
init_codex_sessions();
|
|
8796
|
-
init_copilot_sessions();
|
|
8797
|
-
init_cursor_sessions();
|
|
8798
|
-
init_pi_sessions();
|
|
8799
|
-
init_factory_sessions();
|
|
8800
|
-
init_antigravity_sessions();
|
|
8801
|
-
});
|
|
8802
|
-
// src/hooks/cloud-managed-policies.ts
|
|
8803
|
-
import { createHash as createHash3 } from "node:crypto";
|
|
8804
|
-
import { existsSync as existsSync11, readFileSync as readFileSync11, realpathSync, rmSync } from "node:fs";
|
|
8805
|
-
import { isAbsolute as isAbsolute2, relative as relative3, resolve as resolve11 } from "node:path";
|
|
8806
|
-
function cloudManagedPolicyRoot() {
|
|
8807
|
-
return process.env.FAILPROOFAI_CLOUD_POLICY_DIR ?? cloudPoliciesDir();
|
|
8808
|
-
}
|
|
8809
|
-
function parseManifest(value) {
|
|
8810
|
-
if (!value || typeof value !== "object")
|
|
8811
|
-
throw new Error("active manifest is not an object");
|
|
8812
|
-
const raw = value;
|
|
8813
|
-
if (!ACCEPTED_ACTIVE_SCHEMA_VERSIONS.includes(raw.schemaVersion)) {
|
|
8814
|
-
throw new Error(`unsupported active manifest schema ${String(raw.schemaVersion)} ` + `(supported: ${ACCEPTED_ACTIVE_SCHEMA_VERSIONS.join(", ")})`);
|
|
8815
|
-
}
|
|
8816
|
-
const deployment = raw.deployment ?? raw.generation;
|
|
8817
|
-
if (!Number.isSafeInteger(deployment) || (deployment ?? -1) < 0) {
|
|
8818
|
-
throw new Error("active manifest deployment is invalid");
|
|
8819
|
-
}
|
|
8820
|
-
if (!Array.isArray(raw.policies))
|
|
8821
|
-
throw new Error("active manifest policies is not an array");
|
|
8822
|
-
const policies = raw.policies.map((p) => {
|
|
8823
|
-
const entry = p;
|
|
8824
|
-
return { ...entry, version: entry.version ?? entry.revision };
|
|
8825
|
-
});
|
|
8826
|
-
return { schemaVersion: raw.schemaVersion, deployment, policies };
|
|
8827
|
-
}
|
|
8828
|
-
function resolveManagedPath(root, candidate) {
|
|
8829
|
-
if (!candidate || isAbsolute2(candidate))
|
|
8830
|
-
throw new Error(`unsafe managed policy path ${JSON.stringify(candidate)}`);
|
|
8831
|
-
const absolute = resolve11(root, candidate);
|
|
8832
|
-
const lexicalRelative = relative3(root, absolute);
|
|
8833
|
-
if (lexicalRelative.startsWith("..") || isAbsolute2(lexicalRelative)) {
|
|
8834
|
-
throw new Error(`managed policy path escapes its root: ${JSON.stringify(candidate)}`);
|
|
8835
|
-
}
|
|
8836
|
-
const realRoot = realpathSync(root);
|
|
8837
|
-
const realFile = realpathSync(absolute);
|
|
8838
|
-
const physicalRelative = relative3(realRoot, realFile);
|
|
8839
|
-
if (physicalRelative.startsWith("..") || isAbsolute2(physicalRelative)) {
|
|
8840
|
-
throw new Error(`managed policy symlink escapes its root: ${JSON.stringify(candidate)}`);
|
|
8841
|
-
}
|
|
8842
|
-
return realFile;
|
|
8843
|
-
}
|
|
8844
|
-
function readActiveCloudManagedPolicies() {
|
|
8845
|
-
const root = cloudManagedPolicyRoot();
|
|
8846
|
-
const activePath = resolve11(root, "active.json");
|
|
8847
|
-
if (!existsSync11(activePath))
|
|
8848
|
-
return [];
|
|
8849
|
-
let raw;
|
|
8850
|
-
try {
|
|
8851
|
-
raw = JSON.parse(readFileSync11(activePath, "utf8"));
|
|
8852
|
-
} catch (err) {
|
|
8853
|
-
throw new Error(`failed to read cloud-managed active manifest: ${err instanceof Error ? err.message : String(err)}`);
|
|
8854
|
-
}
|
|
8855
|
-
const manifest = parseManifest(raw);
|
|
8856
|
-
const seen = new Set;
|
|
8857
|
-
return manifest.policies.map((policy) => {
|
|
8858
|
-
if (!POLICY_ID_RE.test(policy.id) || policy.id === "." || policy.id === "..") {
|
|
8859
|
-
throw new Error(`unsafe cloud-managed policy id ${JSON.stringify(policy.id)}`);
|
|
8860
|
-
}
|
|
8861
|
-
if (!Number.isSafeInteger(policy.version) || policy.version < 0) {
|
|
8862
|
-
throw new Error(`invalid version for cloud-managed policy ${policy.id}`);
|
|
8863
|
-
}
|
|
8864
|
-
if (!SHA256_RE.test(policy.sha256)) {
|
|
8865
|
-
throw new Error(`invalid SHA-256 for cloud-managed policy ${policy.id}`);
|
|
8866
|
-
}
|
|
8867
|
-
if (seen.has(policy.id))
|
|
8868
|
-
throw new Error(`duplicate cloud-managed policy id ${policy.id}`);
|
|
8869
|
-
seen.add(policy.id);
|
|
8870
|
-
const path2 = resolveManagedPath(root, policy.path);
|
|
8871
|
-
const bytes = readFileSync11(path2);
|
|
8872
|
-
const actual = createHash3("sha256").update(bytes).digest("hex");
|
|
8873
|
-
if (actual !== policy.sha256) {
|
|
8874
|
-
throw new Error(`cloud-managed policy ${policy.id} failed integrity verification: expected ${policy.sha256}, got ${actual}`);
|
|
8875
|
-
}
|
|
8876
|
-
if (policy.effect !== undefined && policy.effect !== "enforce" && policy.effect !== "observe") {
|
|
8877
|
-
throw new Error(`unknown effect ${JSON.stringify(policy.effect)} for cloud-managed policy ${policy.id}`);
|
|
8878
|
-
}
|
|
8879
|
-
return {
|
|
8880
|
-
id: policy.id,
|
|
8881
|
-
version: policy.version,
|
|
8882
|
-
effect: policy.effect ?? "enforce",
|
|
8883
|
-
sha256: policy.sha256,
|
|
8884
|
-
path: path2,
|
|
8885
|
-
deployment: manifest.deployment
|
|
8886
|
-
};
|
|
8887
|
-
});
|
|
8888
|
-
}
|
|
8889
|
-
var ACCEPTED_ACTIVE_SCHEMA_VERSIONS, SHA256_RE, POLICY_ID_RE;
|
|
8890
|
-
var init_cloud_managed_policies = __esm(() => {
|
|
8891
|
-
init_fp_home();
|
|
8892
|
-
ACCEPTED_ACTIVE_SCHEMA_VERSIONS = [1, 2];
|
|
8893
|
-
SHA256_RE = /^[a-f0-9]{64}$/;
|
|
8894
|
-
POLICY_ID_RE = /^[A-Za-z0-9._-]{1,128}$/;
|
|
8895
|
-
});
|
|
8896
|
-
|
|
8897
|
-
// src/hooks/pack-manifest.ts
|
|
8898
|
-
import { createHash as createHash4 } from "node:crypto";
|
|
8899
|
-
import { existsSync as existsSync12, readFileSync as readFileSync12 } from "node:fs";
|
|
8900
|
-
import { resolve as resolve12 } from "node:path";
|
|
8901
|
-
function packsRoot() {
|
|
8902
|
-
return process.env.FAILPROOFAI_PACK_DIR ?? packsDir();
|
|
8903
|
-
}
|
|
8904
|
-
function parsePackIdentity(value) {
|
|
8905
|
-
if (typeof value.id !== "string" || !PACK_ID_RE.test(value.id)) {
|
|
8906
|
-
throw new Error(`unsafe pack id ${JSON.stringify(value.id)}`);
|
|
8907
|
-
}
|
|
8908
|
-
if (typeof value.version !== "string" || !PACK_VERSION_RE.test(value.version)) {
|
|
8909
|
-
throw new Error(`invalid version for pack ${value.id}`);
|
|
8910
|
-
}
|
|
8911
|
-
if (value.effect !== undefined && value.effect !== "enforce" && value.effect !== "observe") {
|
|
8912
|
-
throw new Error(`unknown effect ${JSON.stringify(value.effect)} for pack ${value.id}`);
|
|
9666
|
+
raw: rawCopy
|
|
9667
|
+
});
|
|
9668
|
+
continue;
|
|
9669
|
+
}
|
|
9670
|
+
entries.push({
|
|
9671
|
+
type: "system",
|
|
9672
|
+
...baseEntry(rawCopy, timestamp, date, source),
|
|
9673
|
+
raw: rawCopy
|
|
9674
|
+
});
|
|
8913
9675
|
}
|
|
8914
|
-
|
|
8915
|
-
|
|
8916
|
-
|
|
8917
|
-
|
|
8918
|
-
effect: value.effect ?? "enforce",
|
|
8919
|
-
...commit ? { commit } : {}
|
|
8920
|
-
};
|
|
9676
|
+
if (entries.length > 500)
|
|
9677
|
+
await new Promise((r) => setImmediate(r));
|
|
9678
|
+
entries.sort((a, b) => a.timestampMs - b.timestampMs);
|
|
9679
|
+
return { entries, rawLines, cwd };
|
|
8921
9680
|
}
|
|
8922
|
-
function
|
|
9681
|
+
async function getPiSessionLog(sessionId) {
|
|
9682
|
+
const filePath = findPiTranscript(sessionId);
|
|
9683
|
+
if (!filePath)
|
|
9684
|
+
return null;
|
|
9685
|
+
let fileContent;
|
|
8923
9686
|
try {
|
|
8924
|
-
|
|
8925
|
-
return Array.isArray(raw.packs) && raw.packs.length > 0;
|
|
9687
|
+
fileContent = await readFile14(filePath, "utf-8");
|
|
8926
9688
|
} catch {
|
|
8927
|
-
return
|
|
8928
|
-
}
|
|
8929
|
-
}
|
|
8930
|
-
function installedFilePath() {
|
|
8931
|
-
return process.env.FAILPROOFAI_PACK_DIR ? resolve12(process.env.FAILPROOFAI_PACK_DIR, "installed.json") : packsInstalledFile();
|
|
8932
|
-
}
|
|
8933
|
-
function parsePackPolicy(packId, value, index) {
|
|
8934
|
-
const where = `${packId} policy #${index}`;
|
|
8935
|
-
if (!value || typeof value !== "object")
|
|
8936
|
-
throw new Error(`${where} is not an object`);
|
|
8937
|
-
const raw = value;
|
|
8938
|
-
const name = raw.name;
|
|
8939
|
-
if (typeof name !== "string" || !PACK_POLICY_NAME_RE.test(name)) {
|
|
8940
|
-
throw new Error(`${where} has an unsafe name ${JSON.stringify(name)}`);
|
|
8941
|
-
}
|
|
8942
|
-
if ("alwaysOn" in raw) {
|
|
8943
|
-
throw new Error(`${where} declares alwaysOn, which packs may not set`);
|
|
8944
|
-
}
|
|
8945
|
-
if (typeof raw.description !== "string" || raw.description.length === 0) {
|
|
8946
|
-
throw new Error(`${where} is missing a description`);
|
|
8947
|
-
}
|
|
8948
|
-
if (typeof raw.category !== "string" || raw.category.length === 0) {
|
|
8949
|
-
throw new Error(`${where} is missing a category`);
|
|
8950
|
-
}
|
|
8951
|
-
if (typeof raw.defaultEnabled !== "boolean") {
|
|
8952
|
-
throw new Error(`${where} is missing defaultEnabled`);
|
|
8953
|
-
}
|
|
8954
|
-
if (!raw.match || typeof raw.match !== "object") {
|
|
8955
|
-
throw new Error(`${where} is missing a match`);
|
|
8956
|
-
}
|
|
8957
|
-
return raw;
|
|
8958
|
-
}
|
|
8959
|
-
function parsePack(root, value) {
|
|
8960
|
-
if (!value || typeof value !== "object")
|
|
8961
|
-
throw new Error("pack entry is not an object");
|
|
8962
|
-
const raw = value;
|
|
8963
|
-
const identity = parsePackIdentity(raw);
|
|
8964
|
-
if (typeof raw.source !== "string" || raw.source.length === 0) {
|
|
8965
|
-
throw new Error(`pack ${raw.id} has no source`);
|
|
8966
|
-
}
|
|
8967
|
-
if (typeof raw.sha256 !== "string" || !SHA256_RE2.test(raw.sha256)) {
|
|
8968
|
-
throw new Error(`invalid SHA-256 for pack ${raw.id}`);
|
|
8969
|
-
}
|
|
8970
|
-
const effect = identity.effect;
|
|
8971
|
-
const path2 = resolveManagedPath(root, raw.entry);
|
|
8972
|
-
const actual = createHash4("sha256").update(readFileSync12(path2)).digest("hex");
|
|
8973
|
-
if (actual !== raw.sha256) {
|
|
8974
|
-
throw new Error(`pack ${raw.id} failed integrity verification: expected ${raw.sha256}, got ${actual}`);
|
|
8975
|
-
}
|
|
8976
|
-
if (!Array.isArray(raw.policies))
|
|
8977
|
-
throw new Error(`pack ${raw.id} policies is not an array`);
|
|
8978
|
-
const policies = raw.policies.map((p, i) => parsePackPolicy(raw.id, p, i));
|
|
8979
|
-
const names = new Set;
|
|
8980
|
-
for (const p of policies) {
|
|
8981
|
-
if (names.has(p.name))
|
|
8982
|
-
throw new Error(`pack ${raw.id} declares ${p.name} twice`);
|
|
8983
|
-
names.add(p.name);
|
|
8984
|
-
}
|
|
8985
|
-
let enabled = null;
|
|
8986
|
-
if (raw.enabled !== undefined) {
|
|
8987
|
-
if (!Array.isArray(raw.enabled) || raw.enabled.some((n) => typeof n !== "string")) {
|
|
8988
|
-
throw new Error(`pack ${raw.id} enabled is not an array of names`);
|
|
8989
|
-
}
|
|
8990
|
-
enabled = raw.enabled.filter((n) => names.has(n));
|
|
9689
|
+
return null;
|
|
8991
9690
|
}
|
|
8992
|
-
let
|
|
8993
|
-
|
|
8994
|
-
|
|
8995
|
-
|
|
8996
|
-
|
|
8997
|
-
clis = raw.clis;
|
|
9691
|
+
let parsed;
|
|
9692
|
+
try {
|
|
9693
|
+
parsed = await parsePiLog(fileContent, "session");
|
|
9694
|
+
} catch {
|
|
9695
|
+
return null;
|
|
8998
9696
|
}
|
|
8999
9697
|
return {
|
|
9000
|
-
|
|
9001
|
-
|
|
9002
|
-
|
|
9003
|
-
|
|
9004
|
-
path: path2,
|
|
9005
|
-
sha256: raw.sha256,
|
|
9006
|
-
effect,
|
|
9007
|
-
policies,
|
|
9008
|
-
enabled
|
|
9698
|
+
entries: parsed.entries,
|
|
9699
|
+
rawLines: parsed.rawLines,
|
|
9700
|
+
cwd: parsed.cwd,
|
|
9701
|
+
filePath
|
|
9009
9702
|
};
|
|
9010
9703
|
}
|
|
9011
|
-
|
|
9012
|
-
|
|
9013
|
-
|
|
9014
|
-
|
|
9015
|
-
|
|
9016
|
-
|
|
9017
|
-
|
|
9018
|
-
|
|
9019
|
-
|
|
9020
|
-
|
|
9021
|
-
|
|
9022
|
-
|
|
9023
|
-
|
|
9024
|
-
if (
|
|
9025
|
-
return
|
|
9026
|
-
|
|
9027
|
-
|
|
9028
|
-
|
|
9029
|
-
|
|
9030
|
-
|
|
9031
|
-
|
|
9032
|
-
|
|
9033
|
-
|
|
9034
|
-
|
|
9035
|
-
|
|
9036
|
-
|
|
9037
|
-
|
|
9038
|
-
|
|
9039
|
-
|
|
9040
|
-
|
|
9041
|
-
|
|
9042
|
-
|
|
9043
|
-
|
|
9044
|
-
|
|
9045
|
-
|
|
9046
|
-
|
|
9047
|
-
|
|
9048
|
-
|
|
9049
|
-
|
|
9050
|
-
|
|
9051
|
-
|
|
9052
|
-
|
|
9053
|
-
packs.push(pack);
|
|
9054
|
-
} catch (err) {
|
|
9055
|
-
const rec = entry;
|
|
9056
|
-
const declaredEffect = rec && (rec.effect === "observe" || rec.effect === "enforce") ? rec.effect : "enforce";
|
|
9057
|
-
errors.push({
|
|
9058
|
-
id: declaredId,
|
|
9059
|
-
reason: errText(err),
|
|
9060
|
-
effect: declaredEffect,
|
|
9061
|
-
...Array.isArray(rec?.policies) ? { declared: safeDeclared(rec.policies) } : {}
|
|
9062
|
-
});
|
|
9063
|
-
}
|
|
9064
|
-
}
|
|
9065
|
-
return { packs, errors };
|
|
9066
|
-
}
|
|
9067
|
-
function safeDeclared(raw) {
|
|
9068
|
-
const out = [];
|
|
9069
|
-
for (const value of raw) {
|
|
9070
|
-
if (!value || typeof value !== "object")
|
|
9071
|
-
continue;
|
|
9072
|
-
const p = value;
|
|
9073
|
-
if (typeof p.name !== "string" || !p.match || typeof p.match !== "object")
|
|
9074
|
-
continue;
|
|
9075
|
-
out.push(p);
|
|
9704
|
+
var UUID_RE, SESSION_FILE_RE2, getCachedPiSessionLog;
|
|
9705
|
+
var init_pi_sessions = __esm(() => {
|
|
9706
|
+
init_log_entries();
|
|
9707
|
+
UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
9708
|
+
SESSION_FILE_RE2 = /^[\d-]+T[\d-]+Z_([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i;
|
|
9709
|
+
getCachedPiSessionLog = runtimeCache((sessionId) => getPiSessionLog(sessionId), 60, { maxSize: 50 });
|
|
9710
|
+
});
|
|
9711
|
+
|
|
9712
|
+
// src/hooks/resolve-transcript-path.ts
|
|
9713
|
+
function resolveTranscriptPath(integration, parsed, sessionId) {
|
|
9714
|
+
const stdinPath = typeof parsed.transcript_path === "string" ? parsed.transcript_path : undefined;
|
|
9715
|
+
if (stdinPath)
|
|
9716
|
+
return stdinPath;
|
|
9717
|
+
if (typeof sessionId !== "string" || sessionId.length === 0)
|
|
9718
|
+
return;
|
|
9719
|
+
switch (integration) {
|
|
9720
|
+
case "claude":
|
|
9721
|
+
return;
|
|
9722
|
+
case "codex":
|
|
9723
|
+
return findCodexTranscript(sessionId) ?? undefined;
|
|
9724
|
+
case "copilot":
|
|
9725
|
+
return findCopilotTranscript(sessionId) ?? undefined;
|
|
9726
|
+
case "cursor":
|
|
9727
|
+
return findCursorTranscript(sessionId) ?? undefined;
|
|
9728
|
+
case "pi":
|
|
9729
|
+
return findPiTranscript(sessionId) ?? undefined;
|
|
9730
|
+
case "factory":
|
|
9731
|
+
return findFactoryTranscript(sessionId) ?? undefined;
|
|
9732
|
+
case "antigravity":
|
|
9733
|
+
return findAntigravityTranscript(sessionId) ?? undefined;
|
|
9734
|
+
case "devin":
|
|
9735
|
+
return `devin-db://${sessionId}`;
|
|
9736
|
+
case "goose":
|
|
9737
|
+
return `goose-db://${sessionId}`;
|
|
9738
|
+
case "opencode":
|
|
9739
|
+
return `opencode-db://${sessionId}`;
|
|
9740
|
+
case "hermes":
|
|
9741
|
+
return;
|
|
9742
|
+
case "openclaw":
|
|
9743
|
+
return;
|
|
9744
|
+
default:
|
|
9745
|
+
return;
|
|
9076
9746
|
}
|
|
9077
|
-
return out;
|
|
9078
|
-
}
|
|
9079
|
-
function errText(err) {
|
|
9080
|
-
return err instanceof Error ? err.message : String(err);
|
|
9081
9747
|
}
|
|
9082
|
-
var
|
|
9083
|
-
|
|
9084
|
-
|
|
9085
|
-
|
|
9086
|
-
|
|
9087
|
-
|
|
9088
|
-
|
|
9089
|
-
PACK_VERSION_RE = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,63}$/;
|
|
9090
|
-
PACK_COMMIT_RE = /^[0-9a-f]{7,40}$/;
|
|
9091
|
-
PACK_POLICY_NAME_RE = /^[A-Za-z0-9._-]{1,128}$/;
|
|
9748
|
+
var init_resolve_transcript_path = __esm(() => {
|
|
9749
|
+
init_codex_sessions();
|
|
9750
|
+
init_copilot_sessions();
|
|
9751
|
+
init_cursor_sessions();
|
|
9752
|
+
init_pi_sessions();
|
|
9753
|
+
init_factory_sessions();
|
|
9754
|
+
init_antigravity_sessions();
|
|
9092
9755
|
});
|
|
9093
|
-
|
|
9094
9756
|
// src/hooks/pack-failclosed.ts
|
|
9757
|
+
function declaredList(value) {
|
|
9758
|
+
if (value === undefined || value === null)
|
|
9759
|
+
return null;
|
|
9760
|
+
if (!Array.isArray(value))
|
|
9761
|
+
return null;
|
|
9762
|
+
if (value.some((entry) => typeof entry !== "string" || entry.length === 0))
|
|
9763
|
+
return null;
|
|
9764
|
+
return value;
|
|
9765
|
+
}
|
|
9095
9766
|
function unionMatch(matchers) {
|
|
9096
9767
|
const events = new Set;
|
|
9097
9768
|
const toolNames = new Set;
|
|
9098
9769
|
let anyEvent = false;
|
|
9099
9770
|
let anyTool = false;
|
|
9100
9771
|
for (const m of matchers) {
|
|
9101
|
-
|
|
9772
|
+
const declaredEvents = declaredList(m?.events);
|
|
9773
|
+
const declaredTools = declaredList(m?.toolNames);
|
|
9774
|
+
if (!declaredEvents || declaredEvents.length === 0)
|
|
9102
9775
|
anyEvent = true;
|
|
9103
9776
|
else
|
|
9104
|
-
for (const e of
|
|
9777
|
+
for (const e of declaredEvents)
|
|
9105
9778
|
events.add(e);
|
|
9106
|
-
if (!
|
|
9779
|
+
if (!declaredTools || declaredTools.length === 0)
|
|
9107
9780
|
anyTool = true;
|
|
9108
9781
|
else
|
|
9109
|
-
for (const t of
|
|
9782
|
+
for (const t of declaredTools)
|
|
9110
9783
|
toolNames.add(t);
|
|
9111
9784
|
}
|
|
9112
9785
|
const scoped = {};
|
|
@@ -9116,11 +9789,20 @@ function unionMatch(matchers) {
|
|
|
9116
9789
|
scoped.toolNames = [...toolNames];
|
|
9117
9790
|
return scoped;
|
|
9118
9791
|
}
|
|
9792
|
+
function outOfScope(clis, cli) {
|
|
9793
|
+
if (!cli)
|
|
9794
|
+
return false;
|
|
9795
|
+
if (!Array.isArray(clis) || clis.length === 0)
|
|
9796
|
+
return false;
|
|
9797
|
+
return !clis.every((c) => typeof c === "string") ? false : !clis.includes(cli);
|
|
9798
|
+
}
|
|
9119
9799
|
function missingGuards(input) {
|
|
9120
9800
|
const out = [];
|
|
9121
9801
|
for (const err of input.errors) {
|
|
9122
9802
|
if (err.effect === "observe")
|
|
9123
9803
|
continue;
|
|
9804
|
+
if (outOfScope(err.clis, input.cli))
|
|
9805
|
+
continue;
|
|
9124
9806
|
const declared = err.declared ?? [];
|
|
9125
9807
|
out.push({
|
|
9126
9808
|
packId: err.id ?? "(unnamed pack)",
|
|
@@ -9133,6 +9815,8 @@ function missingGuards(input) {
|
|
|
9133
9815
|
for (const pack of input.packs) {
|
|
9134
9816
|
if (pack.effect === "observe")
|
|
9135
9817
|
continue;
|
|
9818
|
+
if (outOfScope(pack.clis, input.cli))
|
|
9819
|
+
continue;
|
|
9136
9820
|
const loadFailure = input.failed.get(pack.id);
|
|
9137
9821
|
if (loadFailure && PERMANENT_LOAD_FAILURES.has(loadFailure.type)) {
|
|
9138
9822
|
const taken2 = pack.enabled ?? pack.policies.map((p) => p.name);
|
|
@@ -9165,6 +9849,24 @@ function missingGuards(input) {
|
|
|
9165
9849
|
}
|
|
9166
9850
|
return out.filter((g) => !(g.match.events && g.match.events.length === 0));
|
|
9167
9851
|
}
|
|
9852
|
+
function guardsCover(guards, eventType, toolName) {
|
|
9853
|
+
return guards.some((g) => {
|
|
9854
|
+
const events = g.match.events;
|
|
9855
|
+
if (events && events.length > 0 && !events.includes(eventType)) {
|
|
9856
|
+
return false;
|
|
9857
|
+
}
|
|
9858
|
+
const tools = g.match.toolNames;
|
|
9859
|
+
if (tools && tools.length > 0 && (!toolName || !tools.includes(toolName)))
|
|
9860
|
+
return false;
|
|
9861
|
+
return true;
|
|
9862
|
+
});
|
|
9863
|
+
}
|
|
9864
|
+
function combinedGuardMatch(guards) {
|
|
9865
|
+
if (guards.length === 1)
|
|
9866
|
+
return guards[0].match;
|
|
9867
|
+
const axis = (key) => guards.every((g) => g.match[key]?.length) ? [...new Set(guards.flatMap((g) => g.match[key] ?? []))] : undefined;
|
|
9868
|
+
return { events: axis("events"), toolNames: axis("toolNames") };
|
|
9869
|
+
}
|
|
9168
9870
|
function packFailureReason(guards) {
|
|
9169
9871
|
const named = guards.map((g) => {
|
|
9170
9872
|
const where = g.packVersion ? `${g.packId}@${g.packVersion}` : g.packId;
|
|
@@ -9468,9 +10170,11 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
|
|
|
9468
10170
|
if (pack && enabledBuiltinNames.has(hook.name))
|
|
9469
10171
|
continue;
|
|
9470
10172
|
if (pack) {
|
|
9471
|
-
const
|
|
9472
|
-
|
|
9473
|
-
|
|
10173
|
+
for (const id of loadResult.packAliases?.get(pack.id) ?? [pack.id]) {
|
|
10174
|
+
const seen = registeredByPack.get(id) ?? new Set;
|
|
10175
|
+
seen.add(hook.name);
|
|
10176
|
+
registeredByPack.set(id, seen);
|
|
10177
|
+
}
|
|
9474
10178
|
}
|
|
9475
10179
|
const hookName = hook.name;
|
|
9476
10180
|
const conventionScope = hook.__conventionScope;
|
|
@@ -9526,20 +10230,23 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
|
|
|
9526
10230
|
packs: installedPacks,
|
|
9527
10231
|
registered: registeredByPack,
|
|
9528
10232
|
failed: loadResult.packFailures,
|
|
9529
|
-
disabled: disabledCustomPolicies
|
|
10233
|
+
disabled: disabledCustomPolicies,
|
|
10234
|
+
cli
|
|
9530
10235
|
});
|
|
9531
10236
|
if (guards.length > 0) {
|
|
9532
10237
|
const reason = packFailureReason(guards);
|
|
9533
|
-
const match = guards
|
|
9534
|
-
events: guards.every((g) => g.match.events) ? [...new Set(guards.flatMap((g) => g.match.events ?? []))] : undefined
|
|
9535
|
-
};
|
|
10238
|
+
const match = combinedGuardMatch(guards);
|
|
9536
10239
|
const name = "pack/failproofai-pack-unavailable";
|
|
9537
10240
|
policyAttribution.set(name, {
|
|
9538
10241
|
source: "pack",
|
|
9539
10242
|
packId: guards[0].packId,
|
|
9540
10243
|
...guards[0].packVersion ? { packVersion: guards[0].packVersion } : {}
|
|
9541
10244
|
});
|
|
9542
|
-
registerPolicy(name, "A policy pack this machine enforces could not be loaded", async () =>
|
|
10245
|
+
registerPolicy(name, "A policy pack this machine enforces could not be loaded", async (ctx) => {
|
|
10246
|
+
if (!guardsCover(guards, canonicalEventType, ctx.toolName))
|
|
10247
|
+
return { decision: "allow" };
|
|
10248
|
+
return canonicalEventType === "UserPromptSubmit" ? { decision: "instruct", reason } : { decision: "deny", reason };
|
|
10249
|
+
}, match, 1);
|
|
9543
10250
|
hookLogWarn(reason);
|
|
9544
10251
|
}
|
|
9545
10252
|
}
|