failproofai 1.0.4-beta.7 → 1.0.4
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 +5 -5
- 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/build-manifest.json +2 -2
- 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 +2 -2
- 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/build-manifest.json +2 -2
- 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 +2 -2
- 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.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -4
- 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.nft.json +1 -1
- 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.nft.json +1 -1
- 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/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +4 -37
- package/.next/standalone/.next/server/app/audit/page.js +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/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js +2 -2
- 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/build-manifest.json +2 -2
- 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/build-manifest.json +2 -2
- 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 +3 -3
- 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/build-manifest.json +2 -2
- 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 +4 -4
- 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/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js +3 -3
- 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/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -15
- package/.next/standalone/.next/server/app/settings/page.js +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]__0v1hz15._.js → [externals]__0-9ti7z._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__1nb206a._.js → [externals]__0u3wrvq._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0rv7m0k._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1legmza._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1p-qi2t._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
- package/.next/standalone/.next/server/chunks/{_0otft92._.js → _1-akg6w._.js} +2 -2
- package/.next/standalone/.next/server/chunks/_1ek68ln._.js +10 -10
- package/.next/standalone/.next/server/chunks/_1vslkoe._.js +1 -1
- package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.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_fp-home_ts_09kv0bn._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1fvfw59._.js → [root-of-the-server]__00-s7h8._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__12hbpha._.js → [root-of-the-server]__09flfsx._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0kokc8z._.js → [root-of-the-server]__0n_lxhg._.js} +1 -1
- 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 +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +6 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0dxf928._.js → [root-of-the-server]__190won8._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0n2txhk._.js → [root-of-the-server]__1ctpynv._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/_02yoeej._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_0a0zlpr._.js → _04rbnaq._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_0s-77v5._.js → _0h_douw._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1_ozgif._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1ddpc5g._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1es2j7i._.js +14 -51
- package/.next/standalone/.next/server/chunks/ssr/{_07t3jol._.js → _1u8-lu2._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_1ylae7o._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +69 -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 +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_13n822a._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_1ao30b1.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +5 -5
- 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 +21 -65
- package/.next/standalone/.next/static/chunks/{0f1pznnc7zlub.js → 0-tyrpwuc3lla.js} +1 -1
- package/.next/standalone/.next/static/chunks/0ca7octhod0g2.js +69 -0
- package/.next/standalone/.next/static/chunks/0u64yey446k4f.css +1 -0
- package/.next/standalone/.next/static/chunks/{1csubx9gwhsf6.js → 1j6o9min_6qa9.js} +1 -1
- package/.next/standalone/.next/static/chunks/27cl8h-c2w7cd.js +1 -0
- package/.next/standalone/.next/static/chunks/{0jpd8dv2vk930.css → 29-iqnp9v_axt.css} +1 -1
- package/.next/standalone/.next/static/chunks/{3uyhmm01g64k4.css → 2k9f4tyv04809.css} +0 -1
- package/.next/standalone/.next/static/chunks/{0t6ea7d3bkmdv.js → 2lwipmv5etc8m.js} +1 -1
- package/.next/standalone/.next/static/chunks/2pd16tn5qmx13.js +1 -0
- package/.next/standalone/.next/static/chunks/{0ntdfr2552x-h.js → 2u8vh73tpw28_.js} +1 -1
- package/.next/standalone/.next/static/chunks/{15uon66hpwbgg.js → 31u94yukw9fz-.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2pwfrn6_mw-1u.js → 3p9_8x5ezce-9.js} +1 -1
- package/.next/standalone/.next/static/chunks/3qfoeqhy5l10x.js +1 -0
- package/.next/standalone/.next/static/chunks/{083fk73jtv9a0.js → 3tjji451hrgc0.js} +1 -1
- package/.next/standalone/.next/static/chunks/3zkg2s2vzxc3d.js +1 -0
- package/.next/standalone/.next/static/chunks/{turbopack-3s3c-u_u_l0u9.js → turbopack-3p73obe0xmrxm.js} +1 -1
- package/.next/standalone/.opencode/plugins/failproofai.mjs +27 -51
- package/.next/standalone/app/actions/get-scheduled-audit.ts +0 -5
- package/.next/standalone/app/actions/update-scheduled-audit.ts +57 -18
- package/.next/standalone/app/api/auth/status/route.ts +2 -3
- package/.next/standalone/app/audit/_components/audit-dashboard.tsx +34 -140
- package/.next/standalone/app/audit/_components/audit-poster.tsx +10 -29
- package/.next/standalone/app/audit/_components/come-back-better-section.tsx +1 -6
- package/.next/standalone/app/audit/_components/empty-state.tsx +1 -4
- package/.next/standalone/app/audit/_components/how-to-improve-section.tsx +5 -19
- package/.next/standalone/app/audit/_components/share-templates.ts +44 -110
- package/.next/standalone/app/audit/audit-styles.css +1 -132
- package/.next/standalone/app/settings/page.tsx +1 -1
- package/.next/standalone/app/settings/settings-client.tsx +45 -50
- package/.next/standalone/lib/auth/api-server-client.ts +5 -33
- package/.next/standalone/lib/claude-sessions.ts +27 -101
- package/.next/standalone/lib/codex-sessions.ts +4 -20
- package/.next/standalone/lib/copilot-sessions.ts +1 -32
- package/.next/standalone/lib/cursor-sessions.ts +5 -62
- package/.next/standalone/lib/sqlite-reader.ts +1 -78
- package/.next/standalone/package.json +9 -9
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +1 -1
- package/dist/cli.mjs +500 -1762
- package/dist/worker.mjs +16 -138
- package/lib/auth/api-server-client.ts +5 -33
- package/lib/claude-sessions.ts +27 -101
- package/lib/codex-sessions.ts +4 -20
- package/lib/copilot-sessions.ts +1 -32
- package/lib/cursor-sessions.ts +5 -62
- package/lib/sqlite-reader.ts +1 -78
- package/package.json +9 -9
- package/pi-extension/index.ts +5 -50
- package/src/audit/cache.ts +0 -6
- package/src/audit/cli-adapters/claude.ts +0 -13
- package/src/audit/cli-login.ts +1 -4
- package/src/audit/cli.ts +7 -157
- package/src/audit/harm-report.ts +4 -111
- package/src/audit/index.ts +15 -286
- package/src/audit/machine-store.ts +9 -8
- package/src/audit/redact-example.ts +15 -324
- package/src/audit/report-harm.ts +37 -33
- package/src/audit/report.ts +3 -6
- package/src/audit/schedule-cli.ts +33 -54
- package/src/audit/scoring.ts +0 -49
- package/src/audit/types.ts +0 -52
- package/src/hooks/builtin-policies.ts +2 -67
- package/src/hooks/configure-wizard.ts +0 -55
- package/src/hooks/fp-config.ts +48 -83
- package/src/hooks/fp-home.ts +0 -27
- package/src/hooks/integrations.ts +14 -51
- package/src/hooks/uninstall-cli.ts +2 -17
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0-4ja6q._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__00cr3xu._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__04px77e._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0egr5s1._.js +0 -6
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0i0l1di._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0r3c1_0._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17xnxlx._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ckpmkk._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/_04ng_6x._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0l2hi_d._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_rerun-button_tsx_0blihgw._.js +0 -3
- package/.next/standalone/.next/static/chunks/0divvxng208xo.js +0 -1
- package/.next/standalone/.next/static/chunks/27du9n7ssja7l.js +0 -1
- package/.next/standalone/.next/static/chunks/2h_pcb1y8_jde.js +0 -1
- package/.next/standalone/app/actions/get-leaks.ts +0 -104
- package/.next/standalone/app/audit/_components/leak-section.tsx +0 -192
- package/src/audit/desktop-notify.ts +0 -420
- package/src/audit/leak-fingerprint.ts +0 -198
- package/src/audit/leak-record.ts +0 -248
- package/src/audit/leak-scan.ts +0 -268
- package/src/audit/leak-store.ts +0 -220
- package/src/audit/macos-notifier.ts +0 -298
- package/src/hooks/notice.ts +0 -24
- /package/.next/standalone/.next/static/{YiPsVpjrc-pEQoN7XvhZ0 → dg8VQ7Ejlo0Qsh_x92oIt}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{YiPsVpjrc-pEQoN7XvhZ0 → dg8VQ7Ejlo0Qsh_x92oIt}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{YiPsVpjrc-pEQoN7XvhZ0 → dg8VQ7Ejlo0Qsh_x92oIt}/_ssgManifest.js +0 -0
package/dist/cli.mjs
CHANGED
|
@@ -29,7 +29,7 @@ var __esm = (fn, res, err) => () => {
|
|
|
29
29
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
30
30
|
|
|
31
31
|
// package.json
|
|
32
|
-
var version2 = "1.0.4
|
|
32
|
+
var version2 = "1.0.4", package_default;
|
|
33
33
|
var init_package = __esm(() => {
|
|
34
34
|
package_default = {
|
|
35
35
|
name: "failproofai",
|
|
@@ -154,16 +154,16 @@ var init_package = __esm(() => {
|
|
|
154
154
|
browserslist: "4.28.8"
|
|
155
155
|
},
|
|
156
156
|
optionalDependencies: {
|
|
157
|
-
"@failproofai/failproofaid-linux-x64": "1.0.4
|
|
158
|
-
"@failproofai/failproofaid-linux-arm64": "1.0.4
|
|
159
|
-
"@failproofai/failproofaid-darwin-x64": "1.0.4
|
|
160
|
-
"@failproofai/failproofaid-darwin-arm64": "1.0.4
|
|
157
|
+
"@failproofai/failproofaid-linux-x64": "1.0.4",
|
|
158
|
+
"@failproofai/failproofaid-linux-arm64": "1.0.4",
|
|
159
|
+
"@failproofai/failproofaid-darwin-x64": "1.0.4",
|
|
160
|
+
"@failproofai/failproofaid-darwin-arm64": "1.0.4"
|
|
161
161
|
},
|
|
162
162
|
failproofaidBinaries: {
|
|
163
|
-
"linux-x64": "
|
|
164
|
-
"linux-arm64": "
|
|
165
|
-
"darwin-x64": "
|
|
166
|
-
"darwin-arm64": "
|
|
163
|
+
"linux-x64": "cca1d41ff951784ba084bf2d72cfad0fe8d988f8226e0e7a4538b1fb8d76a5bd",
|
|
164
|
+
"linux-arm64": "55e9c1e2864cbf6057fd0c27e4bc573ca8fba2cd35d480a3db3a55d398e7bec0",
|
|
165
|
+
"darwin-x64": "2a46ec77d2cac14a036f35e832f92f16895239814a5f10f02121f3c313e70574",
|
|
166
|
+
"darwin-arm64": "a0e7ccc6fe24e1017592637192ba8a26c9d05ca839c98bc72a983094c863b39b"
|
|
167
167
|
}
|
|
168
168
|
};
|
|
169
169
|
});
|
|
@@ -206,7 +206,7 @@ function retiredLayoutPaths() {
|
|
|
206
206
|
legacy.localPoliciesDir()
|
|
207
207
|
];
|
|
208
208
|
}
|
|
209
|
-
var LAYOUT_VERSION = 4, at = (...parts) => resolve(failproofaiHome(), ...parts), atHome = (home, ...parts) => home ? resolve(failproofaiHome(home), ...parts) : at(...parts), versionFile = (home) => atHome(home, "VERSION"), configFile = (home) => atHome(home, "config.json"), credentialsFile = (home) => atHome(home, "credentials.json"), binDir = (home) => atHome(home, "bin"), policiesDir = (home) => atHome(home, "policies"), customPoliciesDir, globalPolicyConfigFile = (home) => atHome(home, "policies-config.json"), cloudPoliciesDir = (home) => resolve(policiesDir(home), "cloud-policies"), packsDir = (home) => resolve(policiesDir(home), "packs"), packsInstalledFile = (home) => resolve(packsDir(home), "installed.json"), cursorsDir = (source, home) => source ? resolve(atHome(home, "cursors"), source) : atHome(home, "cursors"), customAgentsDir = (home) => atHome(home, "custom-agents"), auditDir = (home) => atHome(home, "audit"), auditDashboardFile = (home) => resolve(auditDir(home), "dashboard.json"), auditCacheDir = (home) => resolve(auditDir(home), "cache"), auditSessionFile = (home) => resolve(auditDir(home), "session.json"), auditMachineFile = (home) => resolve(auditDir(home), "machine.json"),
|
|
209
|
+
var LAYOUT_VERSION = 4, at = (...parts) => resolve(failproofaiHome(), ...parts), atHome = (home, ...parts) => home ? resolve(failproofaiHome(home), ...parts) : at(...parts), versionFile = (home) => atHome(home, "VERSION"), configFile = (home) => atHome(home, "config.json"), credentialsFile = (home) => atHome(home, "credentials.json"), binDir = (home) => atHome(home, "bin"), policiesDir = (home) => atHome(home, "policies"), customPoliciesDir, globalPolicyConfigFile = (home) => atHome(home, "policies-config.json"), cloudPoliciesDir = (home) => resolve(policiesDir(home), "cloud-policies"), packsDir = (home) => resolve(policiesDir(home), "packs"), packsInstalledFile = (home) => resolve(packsDir(home), "installed.json"), cursorsDir = (source, home) => source ? resolve(atHome(home, "cursors"), source) : atHome(home, "cursors"), customAgentsDir = (home) => atHome(home, "custom-agents"), auditDir = (home) => atHome(home, "audit"), auditDashboardFile = (home) => resolve(auditDir(home), "dashboard.json"), auditCacheDir = (home) => resolve(auditDir(home), "cache"), auditSessionFile = (home) => resolve(auditDir(home), "session.json"), auditMachineFile = (home) => resolve(auditDir(home), "machine.json"), fpcliDir = (home) => atHome(home, "fpcli"), fpcliAuthFile = (home) => resolve(fpcliDir(home), "cli-auth.json"), hookActivityDir = (home) => atHome(home, "hook-activity"), runDir = (home) => atHome(home, "run"), daemonSocket = () => process.env.FAILPROOFAI_DAEMON_SOCKET || resolve(runDir(), "failproofaid.sock"), auditLockFile = (home) => resolve(runDir(home), "audit.lock"), stateDir = (home) => atHome(home, "state"), shimsDir = (home) => resolve(stateDir(home), "shims"), spoolDir = (home) => resolve(stateDir(home), "spool"), failedDir = (home) => resolve(stateDir(home), "failed"), collectorHealthFile = (home) => resolve(stateDir(home), "collector-health.json"), sessionPauseDir = () => resolve(stateDir(), "sessions"), auditScheduleFile = (home) => resolve(auditDir(home), "schedule.json"), telemetryIdFile = (home) => resolve(stateDir(home), "telemetry-id"), launcherMarker = (home) => resolve(stateDir(home), "launcher-configured"), onboardingAttemptFile = (home) => resolve(stateDir(home), "onboarding-attempt.json"), lastVersionFile = (home) => resolve(stateDir(home), "last-version"), onboardingLockFile = (home) => resolve(stateDir(home), "onboarding.lock"), codexSessionPathsFile = (home) => resolve(stateDir(home), "codex-session-paths.json"), logsDir = (home) => atHome(home, "logs"), migrationsDir = (home) => atHome(home, "migrations"), migrationLedgerFile = (home) => resolve(migrationsDir(home), "applied.json"), migrationBackupDir = (layout, home) => resolve(migrationsDir(home), `backup-layout${layout}`), HOME_CLASSES, legacy;
|
|
210
210
|
var init_fp_home = __esm(() => {
|
|
211
211
|
customPoliciesDir = policiesDir;
|
|
212
212
|
HOME_CLASSES = [
|
|
@@ -228,10 +228,8 @@ var init_fp_home = __esm(() => {
|
|
|
228
228
|
{ path: versionFile, class: "identity" },
|
|
229
229
|
{ path: migrationsDir, class: "identity" },
|
|
230
230
|
{ path: auditMachineFile, class: "identity" },
|
|
231
|
-
{ path: auditLeakIdentityFile, class: "identity" },
|
|
232
231
|
{ path: auditDashboardFile, class: "derived" },
|
|
233
232
|
{ path: auditCacheDir, class: "derived" },
|
|
234
|
-
{ path: auditLeaksFile, class: "derived" },
|
|
235
233
|
{ path: auditScheduleFile, class: "derived" },
|
|
236
234
|
{ path: collectorHealthFile, class: "derived" },
|
|
237
235
|
{ path: codexSessionPathsFile, class: "derived" },
|
|
@@ -396,8 +394,7 @@ function projectConfig(parsed) {
|
|
|
396
394
|
const daemon = parsed.daemon ?? {};
|
|
397
395
|
const collector = parsed.collector ?? {};
|
|
398
396
|
const telemetry = parsed.telemetry ?? {};
|
|
399
|
-
const
|
|
400
|
-
const audit = auditPresent ? parsed.audit : {};
|
|
397
|
+
const audit = parsed.audit ?? {};
|
|
401
398
|
return {
|
|
402
399
|
mode: mode === "cloud" ? "cloud" : "oss",
|
|
403
400
|
daemon: { configured: daemon.configured === true },
|
|
@@ -412,8 +409,7 @@ function projectConfig(parsed) {
|
|
|
412
409
|
},
|
|
413
410
|
telemetry: { enabled: telemetry.enabled !== false },
|
|
414
411
|
audit: {
|
|
415
|
-
auto:
|
|
416
|
-
notify: audit.notify !== false,
|
|
412
|
+
auto: audit.auto === true,
|
|
417
413
|
intervalDays: readIntervalDays(audit.interval_days),
|
|
418
414
|
reportsConsentedAt: typeof audit.reports_consented_at === "number" && Number.isFinite(audit.reports_consented_at) ? audit.reports_consented_at : undefined
|
|
419
415
|
}
|
|
@@ -467,7 +463,6 @@ function writeConfig(config, raw) {
|
|
|
467
463
|
...config.telemetry.enabled ? {} : { telemetry: { enabled: false } },
|
|
468
464
|
audit: {
|
|
469
465
|
auto: config.audit.auto,
|
|
470
|
-
notify: config.audit.notify,
|
|
471
466
|
interval_days: config.audit.intervalDays,
|
|
472
467
|
...config.audit.reportsConsentedAt === undefined ? {} : { reports_consented_at: config.audit.reportsConsentedAt }
|
|
473
468
|
}
|
|
@@ -602,7 +597,7 @@ var init_fp_config = __esm(() => {
|
|
|
602
597
|
environment: "local"
|
|
603
598
|
},
|
|
604
599
|
telemetry: { enabled: true },
|
|
605
|
-
audit: { auto: false,
|
|
600
|
+
audit: { auto: false, intervalDays: DEFAULT_AUDIT_INTERVAL_DAYS }
|
|
606
601
|
};
|
|
607
602
|
OWNED_CONFIG_KEYS = [
|
|
608
603
|
["mode", "kind"],
|
|
@@ -616,7 +611,6 @@ var init_fp_config = __esm(() => {
|
|
|
616
611
|
["collector", "sources", "*", "extra_paths"],
|
|
617
612
|
["telemetry", "enabled"],
|
|
618
613
|
["audit", "auto"],
|
|
619
|
-
["audit", "notify"],
|
|
620
614
|
["audit", "interval_days"],
|
|
621
615
|
["audit", "reports_consented_at"]
|
|
622
616
|
];
|
|
@@ -3870,39 +3864,15 @@ var init_builtin_policies = __esm(() => {
|
|
|
3870
3864
|
SHELL_METACHAR_RE = /[;&<>`$()\\]/;
|
|
3871
3865
|
JWT_RE = /eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/;
|
|
3872
3866
|
API_KEY_PATTERNS = [
|
|
3873
|
-
[/sk-ant-api03-[A-Za-z0-9\-_]{20,}/, "Anthropic API key"],
|
|
3874
|
-
[/sk-ant-oat01-[A-Za-z0-9\-_]{20,}/, "Anthropic OAuth token"],
|
|
3875
3867
|
[/sk-ant-[A-Za-z0-9\-_]{20,}/, "Anthropic API key"],
|
|
3876
3868
|
[/sk-proj-[A-Za-z0-9\-_]{20,}/, "OpenAI project API key"],
|
|
3877
|
-
[/
|
|
3878
|
-
[/(?<!\w)sk-admin-[A-Za-z0-9\-_]{20,}/, "OpenAI admin key"],
|
|
3879
|
-
[/sk-or-v1-[A-Za-z0-9\-_]{20,}/, "OpenRouter API key"],
|
|
3880
|
-
[/sk-[A-Za-z0-9]{20,}/, "OpenAI-compatible key (issuer unknown)"],
|
|
3881
|
-
[/github_pat_[A-Za-z0-9_]{82}/, "GitHub fine-grained token"],
|
|
3869
|
+
[/sk-[A-Za-z0-9]{20,}/, "OpenAI API key"],
|
|
3882
3870
|
[/ghp_[A-Za-z0-9]{36}/, "GitHub personal access token"],
|
|
3883
|
-
[/
|
|
3884
|
-
[/ghu_[A-Za-z0-9]{36}/, "GitHub user-to-server token"],
|
|
3885
|
-
[/ghs_[A-Za-z0-9]{36}/, "GitHub server-to-server token"],
|
|
3886
|
-
[/ghr_[A-Za-z0-9]{36}/, "GitHub refresh token"],
|
|
3887
|
-
[/glpat-[A-Za-z0-9\-_]{20,}/, "GitLab personal access token"],
|
|
3871
|
+
[/github_pat_[A-Za-z0-9_]{82}/, "GitHub fine-grained token"],
|
|
3888
3872
|
[/AKIA[A-Z0-9]{16}/, "AWS access key ID"],
|
|
3889
|
-
[/ASIA[A-Z0-9]{16}/, "AWS temporary access key ID"],
|
|
3890
3873
|
[/sk_live_[A-Za-z0-9]{24,}/, "Stripe live secret key"],
|
|
3891
3874
|
[/sk_test_[A-Za-z0-9]{24,}/, "Stripe test secret key"],
|
|
3892
|
-
[/
|
|
3893
|
-
[/AIza[0-9A-Za-z\-_]{35}/, "Google API key"],
|
|
3894
|
-
[/\d{8,10}:[A-Za-z0-9_-]{35}/, "Telegram bot token"],
|
|
3895
|
-
[/xox[baprs]-[A-Za-z0-9-]{10,}/, "Slack token"],
|
|
3896
|
-
[/xapp-[0-9]-[A-Za-z0-9-]{10,}/, "Slack app-level token"],
|
|
3897
|
-
[/hf_[A-Za-z0-9]{30,}/, "Hugging Face token"],
|
|
3898
|
-
[/gsk_[A-Za-z0-9]{40,}/, "Groq API key"],
|
|
3899
|
-
[/r8_[A-Za-z0-9]{30,}/, "Replicate token"],
|
|
3900
|
-
[/xai-[A-Za-z0-9]{40,}/, "xAI API key"],
|
|
3901
|
-
[/npm_[A-Za-z0-9]{36}/, "npm access token"],
|
|
3902
|
-
[/dop_v1_[a-f0-9]{64}/, "DigitalOcean token"],
|
|
3903
|
-
[/shpat_[a-fA-F0-9]{32}/, "Shopify access token"],
|
|
3904
|
-
[/sb_secret_[A-Za-z0-9\-_]{20,}/, "Supabase secret key"],
|
|
3905
|
-
[/sbp_[a-f0-9]{40}/, "Supabase personal token"]
|
|
3875
|
+
[/AIza[0-9A-Za-z\-_]{35}/, "Google API key"]
|
|
3906
3876
|
];
|
|
3907
3877
|
CONNECTION_STRING_RE = /(?:postgresql|postgres|mysql|mongodb(?:\+srv)?|redis|amqps?|smtps?):\/\/[^@\s]+@/;
|
|
3908
3878
|
PRIVATE_KEY_RE = /-----BEGIN (?:[A-Z]+ )?PRIVATE KEY-----/;
|
|
@@ -11682,7 +11652,7 @@ function buildOpenCodePluginShim(binaryPath, scope) {
|
|
|
11682
11652
|
// Re-generate via: failproofai policies --install --cli opencode
|
|
11683
11653
|
// Plugin shim that bridges OpenCode's plugin API to the failproofai binary.
|
|
11684
11654
|
// See: https://opencode.ai/docs/plugins/
|
|
11685
|
-
import {
|
|
11655
|
+
import { spawnSync } from "node:child_process";
|
|
11686
11656
|
|
|
11687
11657
|
// Map opencode bus-event types → canonical failproofai event names.
|
|
11688
11658
|
// (The binary sees PascalCase — the binary's --cli=opencode flag is for
|
|
@@ -11745,52 +11715,18 @@ function canonicalizeToolInput(canonicalToolName, args) {
|
|
|
11745
11715
|
const FAILPROOFAI_BIN = ${escapedBin};
|
|
11746
11716
|
const USE_NPX = ${useNpx};
|
|
11747
11717
|
|
|
11748
|
-
|
|
11749
|
-
* Run failproofai for one event WITHOUT blocking opencode's event loop.
|
|
11750
|
-
*
|
|
11751
|
-
* This was spawnSync, and opencode loads the plugin in-process in the TUI —
|
|
11752
|
-
* so every hook froze rendering and input for the subprocess's full duration,
|
|
11753
|
-
* with a 60s ceiling. The verdicts are unchanged: every caller already awaits
|
|
11754
|
-
* applyDecision, so the deny still lands before the tool runs. What changes
|
|
11755
|
-
* is that the wait is now a promise rather than a blocked thread, and the TUI
|
|
11756
|
-
* keeps painting while it happens.
|
|
11757
|
-
*
|
|
11758
|
-
* Fail-open on spawn error, and on the timeout — a policy that never ran must
|
|
11759
|
-
* not read as a deny.
|
|
11760
|
-
*/
|
|
11761
|
-
async function runFailproofai(eventName, payload, directory) {
|
|
11718
|
+
function runFailproofai(eventName, payload, directory) {
|
|
11762
11719
|
const cmd = USE_NPX ? "npx" : FAILPROOFAI_BIN;
|
|
11763
11720
|
const args = USE_NPX
|
|
11764
11721
|
? ["-y", "failproofai", "--hook", eventName, "--cli", "opencode"]
|
|
11765
11722
|
: ["--hook", eventName, "--cli", "opencode"];
|
|
11766
|
-
|
|
11767
|
-
|
|
11768
|
-
|
|
11769
|
-
|
|
11770
|
-
|
|
11771
|
-
resolveRun({ exitCode: 0, stdout: "", stderr: "" });
|
|
11772
|
-
return;
|
|
11773
|
-
}
|
|
11774
|
-
let stdout = "";
|
|
11775
|
-
let stderr = "";
|
|
11776
|
-
let settled = false;
|
|
11777
|
-
const finish = (exitCode) => {
|
|
11778
|
-
if (settled) return;
|
|
11779
|
-
settled = true;
|
|
11780
|
-
clearTimeout(timer);
|
|
11781
|
-
resolveRun({ exitCode, stdout, stderr });
|
|
11782
|
-
};
|
|
11783
|
-
const timer = setTimeout(() => {
|
|
11784
|
-
try { child.kill("SIGKILL"); } catch { /* already gone */ }
|
|
11785
|
-
finish(0);
|
|
11786
|
-
}, 60_000);
|
|
11787
|
-
child.stdout.on("data", (d) => { stdout += d; });
|
|
11788
|
-
child.stderr.on("data", (d) => { stderr += d; });
|
|
11789
|
-
child.on("error", () => finish(0));
|
|
11790
|
-
child.on("close", (code) => finish(code ?? 0));
|
|
11791
|
-
child.stdin.on("error", () => {});
|
|
11792
|
-
child.stdin.end(JSON.stringify(payload));
|
|
11723
|
+
const r = spawnSync(cmd, args, {
|
|
11724
|
+
input: JSON.stringify(payload),
|
|
11725
|
+
encoding: "utf8",
|
|
11726
|
+
timeout: 60_000,
|
|
11727
|
+
cwd: directory,
|
|
11793
11728
|
});
|
|
11729
|
+
return { exitCode: r.status ?? 0, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
|
|
11794
11730
|
}
|
|
11795
11731
|
|
|
11796
11732
|
async function applyDecision(result, ctx, eventName) {
|
|
@@ -11855,7 +11791,7 @@ export default async function failproofaiPlugin({ client, directory }) {
|
|
|
11855
11791
|
}
|
|
11856
11792
|
}
|
|
11857
11793
|
if (!prompt) prompt = (info.text || info.content || props.text || "").toString();
|
|
11858
|
-
const r =
|
|
11794
|
+
const r = runFailproofai("UserPromptSubmit", {
|
|
11859
11795
|
session_id: sessionID, cwd: directory, hook_event_name: "UserPromptSubmit", prompt,
|
|
11860
11796
|
}, directory);
|
|
11861
11797
|
await applyDecision(r, { client, sessionID }, "UserPromptSubmit");
|
|
@@ -11865,11 +11801,8 @@ export default async function failproofaiPlugin({ client, directory }) {
|
|
|
11865
11801
|
const claudeEvent = BUS_EVENT_MAP[event.type];
|
|
11866
11802
|
if (!claudeEvent) return;
|
|
11867
11803
|
const props = event.properties || {};
|
|
11868
|
-
const
|
|
11869
|
-
|
|
11870
|
-
const sessionID = info.id || info.sessionID || info.sessionId || info.session_id ||
|
|
11871
|
-
props.sessionID || props.sessionId || props.session_id || props.id;
|
|
11872
|
-
const r = await runFailproofai(claudeEvent, {
|
|
11804
|
+
const sessionID = props.sessionID || (props.session && props.session.id) || props.id;
|
|
11805
|
+
const r = runFailproofai(claudeEvent, {
|
|
11873
11806
|
session_id: sessionID, cwd: directory, hook_event_name: claudeEvent,
|
|
11874
11807
|
}, directory);
|
|
11875
11808
|
await applyDecision(r, { client, sessionID }, claudeEvent);
|
|
@@ -11878,7 +11811,7 @@ export default async function failproofaiPlugin({ client, directory }) {
|
|
|
11878
11811
|
// First-class PreToolUse hook. Note: tool args live on output.args (mutable).
|
|
11879
11812
|
"tool.execute.before": async (input, output) => {
|
|
11880
11813
|
const canonicalTool = canonicalizeTool(input.tool);
|
|
11881
|
-
const r =
|
|
11814
|
+
const r = runFailproofai("PreToolUse", {
|
|
11882
11815
|
session_id: input.sessionID,
|
|
11883
11816
|
cwd: directory,
|
|
11884
11817
|
tool_name: canonicalTool,
|
|
@@ -11891,7 +11824,7 @@ export default async function failproofaiPlugin({ client, directory }) {
|
|
|
11891
11824
|
// First-class PostToolUse hook. Note: tool args live on input.args here.
|
|
11892
11825
|
"tool.execute.after": async (input, output) => {
|
|
11893
11826
|
const canonicalTool = canonicalizeTool(input.tool);
|
|
11894
|
-
const r =
|
|
11827
|
+
const r = runFailproofai("PostToolUse", {
|
|
11895
11828
|
session_id: input.sessionID,
|
|
11896
11829
|
cwd: directory,
|
|
11897
11830
|
tool_name: canonicalTool,
|
|
@@ -11905,7 +11838,7 @@ export default async function failproofaiPlugin({ client, directory }) {
|
|
|
11905
11838
|
// Cleaner deny UX for prompted tools — mutate output.status instead of throwing.
|
|
11906
11839
|
"permission.ask": async (input, output) => {
|
|
11907
11840
|
const canonicalTool = canonicalizeTool(input.tool);
|
|
11908
|
-
const r =
|
|
11841
|
+
const r = runFailproofai("PermissionRequest", {
|
|
11909
11842
|
session_id: input.sessionID,
|
|
11910
11843
|
cwd: directory,
|
|
11911
11844
|
tool_name: canonicalTool || input.command || "permission",
|
|
@@ -17177,21 +17110,8 @@ var init_cursor_projects = __esm(() => {
|
|
|
17177
17110
|
// lib/sqlite-reader.ts
|
|
17178
17111
|
import { existsSync as existsSync11, readFileSync as readFileSync8 } from "node:fs";
|
|
17179
17112
|
import initSqlJs from "sql.js/dist/sql-asm.js";
|
|
17180
|
-
function silenceSqliteExperimentalWarning() {
|
|
17181
|
-
if (warningPatched)
|
|
17182
|
-
return;
|
|
17183
|
-
warningPatched = true;
|
|
17184
|
-
const original = process.emitWarning.bind(process);
|
|
17185
|
-
process.emitWarning = (warning, ...rest) => {
|
|
17186
|
-
const text = typeof warning === "string" ? warning : String(warning?.message ?? "");
|
|
17187
|
-
if (text.includes("SQLite is an experimental feature"))
|
|
17188
|
-
return;
|
|
17189
|
-
return original(warning, ...rest);
|
|
17190
|
-
};
|
|
17191
|
-
}
|
|
17192
17113
|
async function tryNodeSqlite(dbPath) {
|
|
17193
17114
|
try {
|
|
17194
|
-
silenceSqliteExperimentalWarning();
|
|
17195
17115
|
const mod = await import("node:sqlite");
|
|
17196
17116
|
const db = new mod.DatabaseSync(dbPath, { readOnly: true });
|
|
17197
17117
|
return {
|
|
@@ -17212,21 +17132,10 @@ function loadSqlJs() {
|
|
|
17212
17132
|
sqlPromise = initSqlJs();
|
|
17213
17133
|
return sqlPromise;
|
|
17214
17134
|
}
|
|
17215
|
-
function clearWalFlag(bytes) {
|
|
17216
|
-
if (bytes.length < 20)
|
|
17217
|
-
return bytes;
|
|
17218
|
-
if (bytes.toString("latin1", 0, 16) !== SQLITE_MAGIC)
|
|
17219
|
-
return bytes;
|
|
17220
|
-
if (bytes[18] === 1 && bytes[19] === 1)
|
|
17221
|
-
return bytes;
|
|
17222
|
-
bytes[18] = 1;
|
|
17223
|
-
bytes[19] = 1;
|
|
17224
|
-
return bytes;
|
|
17225
|
-
}
|
|
17226
17135
|
async function trySqlJs(dbPath) {
|
|
17227
17136
|
try {
|
|
17228
17137
|
const SQL = await loadSqlJs();
|
|
17229
|
-
const db = new SQL.Database(
|
|
17138
|
+
const db = new SQL.Database(readFileSync8(dbPath));
|
|
17230
17139
|
return {
|
|
17231
17140
|
query(sql, params) {
|
|
17232
17141
|
const stmt = db.prepare(sql);
|
|
@@ -17254,7 +17163,7 @@ async function openSqliteReadonly(dbPath) {
|
|
|
17254
17163
|
return null;
|
|
17255
17164
|
return await tryNodeSqlite(dbPath) ?? await trySqlJs(dbPath);
|
|
17256
17165
|
}
|
|
17257
|
-
var
|
|
17166
|
+
var sqlPromise = null;
|
|
17258
17167
|
var init_sqlite_reader = () => {};
|
|
17259
17168
|
|
|
17260
17169
|
// lib/opencode-db.ts
|
|
@@ -20005,15 +19914,6 @@ function joinTexts(blocks, wantedType) {
|
|
|
20005
19914
|
return blocks.filter((b) => b?.type === wantedType && typeof b.text === "string").map((b) => b.text).join(`
|
|
20006
19915
|
`);
|
|
20007
19916
|
}
|
|
20008
|
-
function toolOutputText(output) {
|
|
20009
|
-
if (typeof output === "string")
|
|
20010
|
-
return output;
|
|
20011
|
-
if (Array.isArray(output)) {
|
|
20012
|
-
return output.filter((block) => !!block && typeof block === "object").map((block) => typeof block.text === "string" ? block.text : "").filter(Boolean).join(`
|
|
20013
|
-
`);
|
|
20014
|
-
}
|
|
20015
|
-
return output === undefined ? "" : JSON.stringify(output);
|
|
20016
|
-
}
|
|
20017
19917
|
async function parseCodexLog(fileContent, source = "session") {
|
|
20018
19918
|
const lines = fileContent.split(`
|
|
20019
19919
|
`).filter((line) => line.trim() !== "");
|
|
@@ -20090,11 +19990,10 @@ async function parseCodexLog(fileContent, source = "session") {
|
|
|
20090
19990
|
});
|
|
20091
19991
|
continue;
|
|
20092
19992
|
}
|
|
20093
|
-
if (subType === "function_call"
|
|
19993
|
+
if (subType === "function_call") {
|
|
20094
19994
|
const callId = payload.call_id;
|
|
20095
19995
|
const name = payload.name ?? "function_call";
|
|
20096
|
-
const
|
|
20097
|
-
const input = safeJsonParse4(encodedInput);
|
|
19996
|
+
const input = safeJsonParse4(payload.arguments);
|
|
20098
19997
|
const toolUse = {
|
|
20099
19998
|
type: "tool_use",
|
|
20100
19999
|
id: callId ?? `${timestamp}-${name}`,
|
|
@@ -20113,7 +20012,7 @@ async function parseCodexLog(fileContent, source = "session") {
|
|
|
20113
20012
|
}
|
|
20114
20013
|
continue;
|
|
20115
20014
|
}
|
|
20116
|
-
if (subType === "function_call_output"
|
|
20015
|
+
if (subType === "function_call_output") {
|
|
20117
20016
|
const callId = payload.call_id;
|
|
20118
20017
|
const block = callId ? toolUseById.get(callId) : undefined;
|
|
20119
20018
|
if (block) {
|
|
@@ -20122,7 +20021,7 @@ async function parseCodexLog(fileContent, source = "session") {
|
|
|
20122
20021
|
block.result = {
|
|
20123
20022
|
timestamp,
|
|
20124
20023
|
timestampFormatted: formatTimestamp(date),
|
|
20125
|
-
content:
|
|
20024
|
+
content: typeof payload.output === "string" ? payload.output : JSON.stringify(payload.output),
|
|
20126
20025
|
durationMs: duration,
|
|
20127
20026
|
durationFormatted: formatDuration(duration)
|
|
20128
20027
|
};
|
|
@@ -20317,8 +20216,6 @@ async function parseCopilotLog(fileContent, source = "session") {
|
|
|
20317
20216
|
const rawLines = [];
|
|
20318
20217
|
const toolUseById = new Map;
|
|
20319
20218
|
const toolUseStartMs = new Map;
|
|
20320
|
-
let mostRecentToolUse;
|
|
20321
|
-
let mostRecentToolUseStartMs = 0;
|
|
20322
20219
|
let cwd;
|
|
20323
20220
|
let seenSessionStart = false;
|
|
20324
20221
|
for (let i = 0;i < lines.length; i++) {
|
|
@@ -20414,24 +20311,8 @@ async function parseCopilotLog(fileContent, source = "session") {
|
|
|
20414
20311
|
toolUseById.set(callId, toolUse);
|
|
20415
20312
|
toolUseStartMs.set(callId, date.getTime());
|
|
20416
20313
|
}
|
|
20417
|
-
mostRecentToolUse = toolUse;
|
|
20418
|
-
mostRecentToolUseStartMs = date.getTime();
|
|
20419
20314
|
continue;
|
|
20420
20315
|
}
|
|
20421
|
-
if (recType === "hook.start" && data.hookType === "postToolUse" && mostRecentToolUse) {
|
|
20422
|
-
const input = data.input ?? {};
|
|
20423
|
-
const hookResult = input.toolResult ?? {};
|
|
20424
|
-
const hookContent = hookResult.textResultForLlm ?? hookResult.sessionLog;
|
|
20425
|
-
if (typeof hookContent === "string" && hookContent.length > 0) {
|
|
20426
|
-
mostRecentToolUse.result = {
|
|
20427
|
-
timestamp,
|
|
20428
|
-
timestampFormatted: formatTimestamp(date),
|
|
20429
|
-
content: hookContent,
|
|
20430
|
-
durationMs: Math.max(0, date.getTime() - mostRecentToolUseStartMs),
|
|
20431
|
-
durationFormatted: formatDuration(Math.max(0, date.getTime() - mostRecentToolUseStartMs))
|
|
20432
|
-
};
|
|
20433
|
-
}
|
|
20434
|
-
}
|
|
20435
20316
|
if (recType === "tool.execution_complete") {
|
|
20436
20317
|
const callId = data.toolCallId;
|
|
20437
20318
|
const block = callId ? toolUseById.get(callId) : undefined;
|
|
@@ -20441,8 +20322,7 @@ async function parseCopilotLog(fileContent, source = "session") {
|
|
|
20441
20322
|
const telemetry = data.toolTelemetry ?? {};
|
|
20442
20323
|
const reportedMs = telemetry.metrics?.commandTimeMs ?? telemetry.metrics?.durationMs ?? null;
|
|
20443
20324
|
const durationMs = typeof reportedMs === "number" && reportedMs >= 0 ? reportedMs : Math.max(0, date.getTime() - startMs);
|
|
20444
|
-
const
|
|
20445
|
-
const content = block.result?.content || completionContent;
|
|
20325
|
+
const content = result.detailedContent ?? result.content ?? "";
|
|
20446
20326
|
block.result = {
|
|
20447
20327
|
timestamp,
|
|
20448
20328
|
timestampFormatted: formatTimestamp(date),
|
|
@@ -20566,27 +20446,8 @@ async function parseCursorLog(fileContent, source = "session") {
|
|
|
20566
20446
|
if (!raw.type && raw.role && raw.message?.content) {
|
|
20567
20447
|
const synthDate = new Date(SYNTH_T0 + i);
|
|
20568
20448
|
const synthTs = synthDate.toISOString();
|
|
20569
|
-
const
|
|
20570
|
-
const textParts = messageContent.filter((c) => c?.type === "text" && typeof c.text === "string").map((c) => c.text).join("");
|
|
20449
|
+
const textParts = raw.message.content.filter((c) => c?.type === "text" && typeof c.text === "string").map((c) => c.text).join("");
|
|
20571
20450
|
if (raw.role === "user") {
|
|
20572
|
-
let attachedResult = false;
|
|
20573
|
-
for (const block of messageContent) {
|
|
20574
|
-
if (block?.type !== "tool_result")
|
|
20575
|
-
continue;
|
|
20576
|
-
const callId = typeof block.tool_use_id === "string" ? block.tool_use_id : undefined;
|
|
20577
|
-
const toolUse = callId ? toolUseById.get(callId) : undefined;
|
|
20578
|
-
if (!toolUse)
|
|
20579
|
-
continue;
|
|
20580
|
-
const durationMs = Math.max(0, synthDate.getTime() - (toolUseStartMs.get(callId) ?? synthDate.getTime()));
|
|
20581
|
-
toolUse.result = {
|
|
20582
|
-
timestamp: synthTs,
|
|
20583
|
-
timestampFormatted: formatTimestamp(synthDate),
|
|
20584
|
-
content: typeof block.content === "string" ? block.content : block.content == null ? "" : JSON.stringify(block.content),
|
|
20585
|
-
durationMs,
|
|
20586
|
-
durationFormatted: formatDuration(durationMs)
|
|
20587
|
-
};
|
|
20588
|
-
attachedResult = true;
|
|
20589
|
-
}
|
|
20590
20451
|
const queryMatch = /<user_query>\s*([\s\S]*?)\s*<\/user_query>/.exec(textParts);
|
|
20591
20452
|
const text = queryMatch ? queryMatch[1] : textParts;
|
|
20592
20453
|
if (text) {
|
|
@@ -20595,35 +20456,11 @@ async function parseCursorLog(fileContent, source = "session") {
|
|
|
20595
20456
|
...baseEntry(rawCopy, synthTs, synthDate, source),
|
|
20596
20457
|
message: { role: "user", content: text }
|
|
20597
20458
|
});
|
|
20598
|
-
} else if (!attachedResult) {
|
|
20599
|
-
entries.push({
|
|
20600
|
-
type: "system",
|
|
20601
|
-
...baseEntry(rawCopy, synthTs, synthDate, source),
|
|
20602
|
-
raw: rawCopy
|
|
20603
|
-
});
|
|
20604
20459
|
}
|
|
20605
20460
|
continue;
|
|
20606
20461
|
}
|
|
20607
20462
|
if (raw.role === "assistant") {
|
|
20608
|
-
const blocks = [];
|
|
20609
|
-
if (textParts)
|
|
20610
|
-
blocks.push({ type: "text", text: textParts });
|
|
20611
|
-
for (let j = 0;j < messageContent.length; j++) {
|
|
20612
|
-
const contentBlock = messageContent[j];
|
|
20613
|
-
if (contentBlock?.type !== "tool_use")
|
|
20614
|
-
continue;
|
|
20615
|
-
const name = typeof contentBlock.name === "string" ? contentBlock.name : "tool";
|
|
20616
|
-
const id = typeof contentBlock.id === "string" && contentBlock.id ? contentBlock.id : `cursor-${i}-${name}-${j}`;
|
|
20617
|
-
const toolUse = {
|
|
20618
|
-
type: "tool_use",
|
|
20619
|
-
id,
|
|
20620
|
-
name,
|
|
20621
|
-
input: contentBlock.input && typeof contentBlock.input === "object" ? contentBlock.input : {}
|
|
20622
|
-
};
|
|
20623
|
-
blocks.push(toolUse);
|
|
20624
|
-
toolUseById.set(id, toolUse);
|
|
20625
|
-
toolUseStartMs.set(id, synthDate.getTime());
|
|
20626
|
-
}
|
|
20463
|
+
const blocks = textParts ? [{ type: "text", text: textParts }] : [];
|
|
20627
20464
|
if (blocks.length === 0) {
|
|
20628
20465
|
entries.push({
|
|
20629
20466
|
type: "system",
|
|
@@ -25112,232 +24949,6 @@ var init_manager = __esm(() => {
|
|
|
25112
24949
|
VALID_POLICY_NAMES = new Set(BUILTIN_POLICIES.map((p) => p.name));
|
|
25113
24950
|
});
|
|
25114
24951
|
|
|
25115
|
-
// src/audit/leak-fingerprint.ts
|
|
25116
|
-
function matchPublicPrefix(value) {
|
|
25117
|
-
for (const entry of PUBLIC_PREFIXES) {
|
|
25118
|
-
if (value.startsWith(entry[0]))
|
|
25119
|
-
return entry;
|
|
25120
|
-
}
|
|
25121
|
-
return null;
|
|
25122
|
-
}
|
|
25123
|
-
function fingerprintSecret(value, fallbackLabel = "secret") {
|
|
25124
|
-
const length = value.length;
|
|
25125
|
-
const matched = matchPublicPrefix(value);
|
|
25126
|
-
if (!matched) {
|
|
25127
|
-
return {
|
|
25128
|
-
display: `[${length}-char ${fallbackLabel}]`,
|
|
25129
|
-
label: fallbackLabel,
|
|
25130
|
-
length,
|
|
25131
|
-
attributed: false
|
|
25132
|
-
};
|
|
25133
|
-
}
|
|
25134
|
-
const [prefix, label] = matched;
|
|
25135
|
-
const bodyLength = length - prefix.length;
|
|
25136
|
-
const run = MASK_CHAR.repeat(Math.max(1, Math.min(MAX_MASK_RUN, bodyLength)));
|
|
25137
|
-
if (length >= MIN_LEN_FOR_TAIL && bodyLength > TAIL_CHARS * 2) {
|
|
25138
|
-
return {
|
|
25139
|
-
display: `${prefix}${run}${value.slice(-TAIL_CHARS)}`,
|
|
25140
|
-
label,
|
|
25141
|
-
length,
|
|
25142
|
-
attributed: true
|
|
25143
|
-
};
|
|
25144
|
-
}
|
|
25145
|
-
return { display: `${prefix}${run}`, label, length, attributed: true };
|
|
25146
|
-
}
|
|
25147
|
-
function isFindingId(id) {
|
|
25148
|
-
return /^[0-9a-f]{16}$/.test(id);
|
|
25149
|
-
}
|
|
25150
|
-
function fingerprintId(value, machineSalt) {
|
|
25151
|
-
const { createHmac } = __require("node:crypto");
|
|
25152
|
-
return createHmac("sha256", machineSalt).update(value).digest("hex").slice(0, 16);
|
|
25153
|
-
}
|
|
25154
|
-
var MASK_CHAR = "•", MAX_MASK_RUN = 8, MIN_LEN_FOR_TAIL = 24, TAIL_CHARS = 4, PUBLIC_PREFIXES;
|
|
25155
|
-
var init_leak_fingerprint = __esm(() => {
|
|
25156
|
-
PUBLIC_PREFIXES = [
|
|
25157
|
-
["sk-ant-api03-", "Anthropic API key"],
|
|
25158
|
-
["sk-ant-oat01-", "Anthropic OAuth token"],
|
|
25159
|
-
["sk-ant-", "Anthropic key"],
|
|
25160
|
-
["sk-proj-", "OpenAI project key"],
|
|
25161
|
-
["sk-svcacct-", "OpenAI service-account key"],
|
|
25162
|
-
["sk-admin-", "OpenAI admin key"],
|
|
25163
|
-
["sk-or-v1-", "OpenRouter key"],
|
|
25164
|
-
["github_pat_", "GitHub fine-grained token"],
|
|
25165
|
-
["ghp_", "GitHub personal access token"],
|
|
25166
|
-
["gho_", "GitHub OAuth token"],
|
|
25167
|
-
["ghu_", "GitHub user-to-server token"],
|
|
25168
|
-
["ghs_", "GitHub server-to-server token"],
|
|
25169
|
-
["ghr_", "GitHub refresh token"],
|
|
25170
|
-
["glpat-", "GitLab personal access token"],
|
|
25171
|
-
["xoxb-", "Slack bot token"],
|
|
25172
|
-
["xoxp-", "Slack user token"],
|
|
25173
|
-
["xapp-", "Slack app-level token"],
|
|
25174
|
-
["sk_live_", "Stripe live secret key"],
|
|
25175
|
-
["sk_test_", "Stripe test secret key"],
|
|
25176
|
-
["rk_live_", "Stripe restricted key"],
|
|
25177
|
-
["AKIA", "AWS access key ID"],
|
|
25178
|
-
["ASIA", "AWS temporary access key ID"],
|
|
25179
|
-
["AIza", "Google API key"],
|
|
25180
|
-
["hf_", "Hugging Face token"],
|
|
25181
|
-
["gsk_", "Groq API key"],
|
|
25182
|
-
["r8_", "Replicate token"],
|
|
25183
|
-
["xai-", "xAI key"],
|
|
25184
|
-
["npm_", "npm access token"],
|
|
25185
|
-
["dop_v1_", "DigitalOcean token"],
|
|
25186
|
-
["shpat_", "Shopify access token"],
|
|
25187
|
-
["sbp_", "Supabase token"]
|
|
25188
|
-
];
|
|
25189
|
-
});
|
|
25190
|
-
|
|
25191
|
-
// src/audit/macos-notifier.ts
|
|
25192
|
-
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
25193
|
-
import { chmodSync as chmodSync5, existsSync as existsSync26, mkdirSync as mkdirSync17, readdirSync as readdirSync16, renameSync as renameSync8, rmSync as rmSync9, writeFileSync as writeFileSync15 } from "node:fs";
|
|
25194
|
-
import { homedir as homedir24 } from "node:os";
|
|
25195
|
-
import { resolve as resolve22 } from "node:path";
|
|
25196
|
-
function macNotifierAppPath(home) {
|
|
25197
|
-
return resolve22(runDir(home), "..", "bin", "FailproofAI Notifier.app");
|
|
25198
|
-
}
|
|
25199
|
-
function macNotifierRunnerPath(home) {
|
|
25200
|
-
return resolve22(runDir(home), "..", "bin", "failproofai-notifier.zsh");
|
|
25201
|
-
}
|
|
25202
|
-
function macNotifierPlistPath() {
|
|
25203
|
-
return resolve22(homedir24(), "Library", "LaunchAgents", `${MAC_NOTIFIER_LABEL}.plist`);
|
|
25204
|
-
}
|
|
25205
|
-
function escapeXml2(s) {
|
|
25206
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
25207
|
-
}
|
|
25208
|
-
function notifierScript(home) {
|
|
25209
|
-
const dir = macNotifyDir(home);
|
|
25210
|
-
const quotedDir = `'${dir.replace(/'/g, `'\\''`)}'`;
|
|
25211
|
-
return `#!/bin/zsh
|
|
25212
|
-
set -u
|
|
25213
|
-
setopt NULL_GLOB
|
|
25214
|
-
|
|
25215
|
-
drop_dir=${quotedDir}
|
|
25216
|
-
|
|
25217
|
-
for entry_path in "$drop_dir"/*; do
|
|
25218
|
-
[ -f "$entry_path" ] || continue
|
|
25219
|
-
note_title=$(/usr/bin/sed -n '1p' "$entry_path")
|
|
25220
|
-
note_body=$(/usr/bin/sed -n '2,$p' "$entry_path" | /usr/bin/tr '\\n' ' ')
|
|
25221
|
-
/bin/rm -f "$entry_path"
|
|
25222
|
-
[ -n "$note_title" ] || continue
|
|
25223
|
-
/usr/bin/osascript - "$note_title" "$note_body" <<'APPLESCRIPT'
|
|
25224
|
-
on run argv
|
|
25225
|
-
display notification (item 2 of argv) with title (item 1 of argv)
|
|
25226
|
-
end run
|
|
25227
|
-
APPLESCRIPT
|
|
25228
|
-
done
|
|
25229
|
-
`;
|
|
25230
|
-
}
|
|
25231
|
-
function notifierPlistContents(runnerPath, watchDir) {
|
|
25232
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
25233
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
25234
|
-
<plist version="1.0">
|
|
25235
|
-
<dict>
|
|
25236
|
-
<key>Label</key>
|
|
25237
|
-
<string>${escapeXml2(MAC_NOTIFIER_LABEL)}</string>
|
|
25238
|
-
<key>ProgramArguments</key>
|
|
25239
|
-
<array>
|
|
25240
|
-
<string>${escapeXml2(runnerPath)}</string>
|
|
25241
|
-
</array>
|
|
25242
|
-
<!-- Started only when something is queued, and it exits when the queue is
|
|
25243
|
-
empty. RunAtLoad would fire it once at login with nothing to say. -->
|
|
25244
|
-
<key>WatchPaths</key>
|
|
25245
|
-
<array>
|
|
25246
|
-
<string>${escapeXml2(watchDir)}</string>
|
|
25247
|
-
</array>
|
|
25248
|
-
<key>RunAtLoad</key>
|
|
25249
|
-
<false/>
|
|
25250
|
-
<!-- Not a service: it does one pass and exits, and launchd must not treat
|
|
25251
|
-
that as a crash to restart. -->
|
|
25252
|
-
<key>KeepAlive</key>
|
|
25253
|
-
<false/>
|
|
25254
|
-
<key>ProcessType</key>
|
|
25255
|
-
<string>Interactive</string>
|
|
25256
|
-
</dict>
|
|
25257
|
-
</plist>
|
|
25258
|
-
`;
|
|
25259
|
-
}
|
|
25260
|
-
function run(cmd, args) {
|
|
25261
|
-
execFileSync3(cmd, args, { stdio: "ignore", timeout: 20000 });
|
|
25262
|
-
}
|
|
25263
|
-
function installMacNotifier(home) {
|
|
25264
|
-
if (process.platform !== "darwin")
|
|
25265
|
-
return { installed: false, reason: "not macOS" };
|
|
25266
|
-
try {
|
|
25267
|
-
const runner = macNotifierRunnerPath(home);
|
|
25268
|
-
const watch = macNotifyDir(home);
|
|
25269
|
-
mkdirSync17(watch, { recursive: true, mode: 448 });
|
|
25270
|
-
mkdirSync17(resolve22(runner, ".."), { recursive: true });
|
|
25271
|
-
writeFileSync15(runner, notifierScript(home), { mode: 448 });
|
|
25272
|
-
chmodSync5(runner, 448);
|
|
25273
|
-
rmSync9(macNotifierAppPath(home), { recursive: true, force: true });
|
|
25274
|
-
mkdirSync17(resolve22(macNotifierPlistPath(), ".."), { recursive: true });
|
|
25275
|
-
writeFileSync15(macNotifierPlistPath(), notifierPlistContents(runner, watch), { mode: 420 });
|
|
25276
|
-
const target = `gui/${process.getuid?.() ?? 0}`;
|
|
25277
|
-
try {
|
|
25278
|
-
run("launchctl", ["bootout", `${target}/${MAC_NOTIFIER_LABEL}`]);
|
|
25279
|
-
} catch {}
|
|
25280
|
-
run("launchctl", ["bootstrap", target, macNotifierPlistPath()]);
|
|
25281
|
-
return { installed: true };
|
|
25282
|
-
} catch (err) {
|
|
25283
|
-
return { installed: false, reason: err instanceof Error ? err.message : String(err) };
|
|
25284
|
-
}
|
|
25285
|
-
}
|
|
25286
|
-
function uninstallMacNotifier(home) {
|
|
25287
|
-
if (process.platform !== "darwin")
|
|
25288
|
-
return;
|
|
25289
|
-
try {
|
|
25290
|
-
run("launchctl", ["bootout", `gui/${process.getuid?.() ?? 0}/${MAC_NOTIFIER_LABEL}`]);
|
|
25291
|
-
} catch {}
|
|
25292
|
-
rmSync9(macNotifierPlistPath(), { force: true });
|
|
25293
|
-
rmSync9(macNotifierAppPath(home), { recursive: true, force: true });
|
|
25294
|
-
rmSync9(macNotifierRunnerPath(home), { force: true });
|
|
25295
|
-
rmSync9(macNotifyDir(home), { recursive: true, force: true });
|
|
25296
|
-
}
|
|
25297
|
-
function queueMacNotification(id, title, body, home) {
|
|
25298
|
-
if (!isFindingId(id))
|
|
25299
|
-
return false;
|
|
25300
|
-
const dir = macNotifyDir(home);
|
|
25301
|
-
const tmp = resolve22(dir, "..", `notify-${id}.tmp`);
|
|
25302
|
-
try {
|
|
25303
|
-
mkdirSync17(dir, { recursive: true, mode: 448 });
|
|
25304
|
-
const payload = `${title.replace(/\s+/g, " ")}
|
|
25305
|
-
${body.replace(/\s+/g, " ")}
|
|
25306
|
-
`;
|
|
25307
|
-
writeFileSync15(tmp, payload, { mode: 384 });
|
|
25308
|
-
renameSync8(tmp, resolve22(dir, id));
|
|
25309
|
-
return true;
|
|
25310
|
-
} catch {
|
|
25311
|
-
try {
|
|
25312
|
-
rmSync9(tmp, { force: true });
|
|
25313
|
-
} catch {}
|
|
25314
|
-
return false;
|
|
25315
|
-
}
|
|
25316
|
-
}
|
|
25317
|
-
function macNotifierInstalled(home) {
|
|
25318
|
-
return existsSync26(macNotifierPlistPath()) && existsSync26(macNotifierRunnerPath(home));
|
|
25319
|
-
}
|
|
25320
|
-
function pruneMacNotifyQueue(home, maxAgeMs = 7 * 86400000) {
|
|
25321
|
-
try {
|
|
25322
|
-
const dir = macNotifyDir(home);
|
|
25323
|
-
if (!existsSync26(dir))
|
|
25324
|
-
return;
|
|
25325
|
-
const { statSync } = __require("node:fs");
|
|
25326
|
-
for (const name of readdirSync16(dir)) {
|
|
25327
|
-
const path = resolve22(dir, name);
|
|
25328
|
-
try {
|
|
25329
|
-
if (Date.now() - statSync(path).mtimeMs > maxAgeMs)
|
|
25330
|
-
rmSync9(path, { force: true });
|
|
25331
|
-
} catch {}
|
|
25332
|
-
}
|
|
25333
|
-
} catch {}
|
|
25334
|
-
}
|
|
25335
|
-
var MAC_NOTIFIER_LABEL = "ai.failproof.notifier", macNotifyDir = (home) => resolve22(runDir(home), "notify");
|
|
25336
|
-
var init_macos_notifier = __esm(() => {
|
|
25337
|
-
init_fp_home();
|
|
25338
|
-
init_leak_fingerprint();
|
|
25339
|
-
});
|
|
25340
|
-
|
|
25341
24952
|
// lib/fetch-with-timeout.ts
|
|
25342
24953
|
function fetchWithTimeout(input, init = {}, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
|
|
25343
24954
|
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
@@ -25354,7 +24965,7 @@ var DEFAULT_FETCH_TIMEOUT_MS = 15000;
|
|
|
25354
24965
|
|
|
25355
24966
|
// src/hooks/cloud-enrollment.ts
|
|
25356
24967
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
25357
|
-
import { existsSync as
|
|
24968
|
+
import { existsSync as existsSync26, readFileSync as readFileSync22, rmSync as rmSync9 } from "node:fs";
|
|
25358
24969
|
import { hostname as hostname4 } from "node:os";
|
|
25359
24970
|
function cloudCredentialPath() {
|
|
25360
24971
|
return process.env.FAILPROOFAI_CLOUD_CREDENTIALS ?? credentialsFile();
|
|
@@ -25393,7 +25004,7 @@ function readCloudCredentials() {
|
|
|
25393
25004
|
if (!usingJsonOverride())
|
|
25394
25005
|
return readCredentials().cloud ?? null;
|
|
25395
25006
|
const path = cloudCredentialPath();
|
|
25396
|
-
if (!
|
|
25007
|
+
if (!existsSync26(path))
|
|
25397
25008
|
return null;
|
|
25398
25009
|
try {
|
|
25399
25010
|
const raw = JSON.parse(readFileSync22(path, "utf8"));
|
|
@@ -25435,10 +25046,10 @@ function resolveMachineLabel(explicit) {
|
|
|
25435
25046
|
function clearCloudCredentials() {
|
|
25436
25047
|
if (usingJsonOverride()) {
|
|
25437
25048
|
const path = cloudCredentialPath();
|
|
25438
|
-
if (!
|
|
25049
|
+
if (!existsSync26(path))
|
|
25439
25050
|
return false;
|
|
25440
25051
|
try {
|
|
25441
|
-
|
|
25052
|
+
rmSync9(path);
|
|
25442
25053
|
return true;
|
|
25443
25054
|
} catch {
|
|
25444
25055
|
return false;
|
|
@@ -25686,14 +25297,14 @@ var init_cloud_connection = __esm(() => {
|
|
|
25686
25297
|
});
|
|
25687
25298
|
|
|
25688
25299
|
// src/hooks/setup-state.ts
|
|
25689
|
-
import { existsSync as
|
|
25690
|
-
import { dirname as dirname14, resolve as
|
|
25691
|
-
import { homedir as
|
|
25692
|
-
function globalConfigPath(home =
|
|
25300
|
+
import { existsSync as existsSync27, realpathSync as realpathSync2, statSync as statSync17 } from "node:fs";
|
|
25301
|
+
import { dirname as dirname14, resolve as resolve22 } from "node:path";
|
|
25302
|
+
import { homedir as homedir24 } from "node:os";
|
|
25303
|
+
function globalConfigPath(home = homedir24()) {
|
|
25693
25304
|
return globalPolicyConfigFile(home);
|
|
25694
25305
|
}
|
|
25695
25306
|
function projectConfigPath(projectRoot) {
|
|
25696
|
-
return
|
|
25307
|
+
return resolve22(projectRoot, ".failproofai", "policies-config.json");
|
|
25697
25308
|
}
|
|
25698
25309
|
function legacyMarkerPath(home) {
|
|
25699
25310
|
return launcherMarker(home);
|
|
@@ -25705,18 +25316,18 @@ function isDir(path) {
|
|
|
25705
25316
|
return false;
|
|
25706
25317
|
}
|
|
25707
25318
|
}
|
|
25708
|
-
function findProjectRoot(start, home =
|
|
25319
|
+
function findProjectRoot(start, home = homedir24()) {
|
|
25709
25320
|
const resolvedHome = realResolve(home);
|
|
25710
25321
|
let dir = realResolve(start);
|
|
25711
25322
|
while (dir !== resolvedHome) {
|
|
25712
|
-
if (isDir(
|
|
25323
|
+
if (isDir(resolve22(dir, ".failproofai")))
|
|
25713
25324
|
return dir;
|
|
25714
25325
|
const parent = dirname14(dir);
|
|
25715
25326
|
if (parent === dir)
|
|
25716
25327
|
break;
|
|
25717
25328
|
dir = parent;
|
|
25718
25329
|
}
|
|
25719
|
-
return
|
|
25330
|
+
return resolve22(start);
|
|
25720
25331
|
}
|
|
25721
25332
|
function hasGlobalHooksInstalled() {
|
|
25722
25333
|
for (const id of INTEGRATION_TYPES) {
|
|
@@ -25728,14 +25339,14 @@ function hasGlobalHooksInstalled() {
|
|
|
25728
25339
|
return false;
|
|
25729
25340
|
}
|
|
25730
25341
|
function realResolve(path) {
|
|
25731
|
-
const lexical =
|
|
25342
|
+
const lexical = resolve22(path);
|
|
25732
25343
|
try {
|
|
25733
25344
|
return realpathSync2(lexical);
|
|
25734
25345
|
} catch {
|
|
25735
25346
|
return lexical;
|
|
25736
25347
|
}
|
|
25737
25348
|
}
|
|
25738
|
-
function detectSetupState(cwd = process.cwd(), home =
|
|
25349
|
+
function detectSetupState(cwd = process.cwd(), home = homedir24()) {
|
|
25739
25350
|
const resolvedCwd = realResolve(cwd);
|
|
25740
25351
|
const resolvedHome = realResolve(home);
|
|
25741
25352
|
const projectRoot = findProjectRoot(resolvedCwd, resolvedHome);
|
|
@@ -25743,10 +25354,10 @@ function detectSetupState(cwd = process.cwd(), home = homedir25()) {
|
|
|
25743
25354
|
cwd: resolvedCwd,
|
|
25744
25355
|
home: resolvedHome,
|
|
25745
25356
|
projectRoot,
|
|
25746
|
-
hasGlobalConfig:
|
|
25747
|
-
hasProjectConfig: projectRoot !== resolvedHome &&
|
|
25357
|
+
hasGlobalConfig: existsSync27(globalConfigPath(resolvedHome)),
|
|
25358
|
+
hasProjectConfig: projectRoot !== resolvedHome && existsSync27(projectConfigPath(projectRoot)),
|
|
25748
25359
|
hasGlobalHooks: hasGlobalHooksInstalled(),
|
|
25749
|
-
hasLegacyMarker:
|
|
25360
|
+
hasLegacyMarker: existsSync27(legacyMarkerPath(resolvedHome)),
|
|
25750
25361
|
inHomeDir: resolvedCwd === resolvedHome
|
|
25751
25362
|
};
|
|
25752
25363
|
}
|
|
@@ -25763,11 +25374,11 @@ var init_setup_state = __esm(() => {
|
|
|
25763
25374
|
});
|
|
25764
25375
|
|
|
25765
25376
|
// src/hooks/onboarding-attempt.ts
|
|
25766
|
-
import { existsSync as
|
|
25377
|
+
import { existsSync as existsSync28, mkdirSync as mkdirSync17, readFileSync as readFileSync23, rmSync as rmSync10, writeFileSync as writeFileSync15 } from "node:fs";
|
|
25767
25378
|
import { dirname as dirname15 } from "node:path";
|
|
25768
25379
|
function readOnboardingAttempt() {
|
|
25769
25380
|
const path = onboardingAttemptFile();
|
|
25770
|
-
if (!
|
|
25381
|
+
if (!existsSync28(path))
|
|
25771
25382
|
return null;
|
|
25772
25383
|
try {
|
|
25773
25384
|
const raw = JSON.parse(readFileSync23(path, "utf8"));
|
|
@@ -25789,7 +25400,7 @@ function readOnboardingAttempt() {
|
|
|
25789
25400
|
function recordOnboardingAttempt(reason, cliVersion, daemonStatus, now = Date.now()) {
|
|
25790
25401
|
try {
|
|
25791
25402
|
const path = onboardingAttemptFile();
|
|
25792
|
-
|
|
25403
|
+
mkdirSync17(dirname15(path), { recursive: true });
|
|
25793
25404
|
const record = {
|
|
25794
25405
|
schemaVersion: SCHEMA_VERSION3,
|
|
25795
25406
|
reason,
|
|
@@ -25797,12 +25408,12 @@ function recordOnboardingAttempt(reason, cliVersion, daemonStatus, now = Date.no
|
|
|
25797
25408
|
daemonStatus,
|
|
25798
25409
|
at: now
|
|
25799
25410
|
};
|
|
25800
|
-
|
|
25411
|
+
writeFileSync15(path, JSON.stringify(record, null, 2), "utf8");
|
|
25801
25412
|
} catch {}
|
|
25802
25413
|
}
|
|
25803
25414
|
function clearOnboardingAttempt() {
|
|
25804
25415
|
try {
|
|
25805
|
-
|
|
25416
|
+
rmSync10(onboardingAttemptFile(), { force: true });
|
|
25806
25417
|
} catch {}
|
|
25807
25418
|
}
|
|
25808
25419
|
function blockerCleared(attempt, probe) {
|
|
@@ -25844,9 +25455,9 @@ var init_onboarding_attempt = __esm(() => {
|
|
|
25844
25455
|
});
|
|
25845
25456
|
|
|
25846
25457
|
// src/hooks/onboarding-lock.ts
|
|
25847
|
-
import { mkdirSync as
|
|
25848
|
-
import { homedir as
|
|
25849
|
-
function onboardingLockPath(home =
|
|
25458
|
+
import { mkdirSync as mkdirSync18, readFileSync as readFileSync24, rmSync as rmSync11, writeFileSync as writeFileSync16 } from "node:fs";
|
|
25459
|
+
import { homedir as homedir25 } from "node:os";
|
|
25460
|
+
function onboardingLockPath(home = homedir25()) {
|
|
25850
25461
|
return onboardingLockFile(home);
|
|
25851
25462
|
}
|
|
25852
25463
|
function processAlive(pid) {
|
|
@@ -25869,15 +25480,15 @@ function readLock(path) {
|
|
|
25869
25480
|
return null;
|
|
25870
25481
|
}
|
|
25871
25482
|
}
|
|
25872
|
-
function acquireOnboardingLock(home =
|
|
25483
|
+
function acquireOnboardingLock(home = homedir25()) {
|
|
25873
25484
|
const path = onboardingLockPath(home);
|
|
25874
25485
|
try {
|
|
25875
25486
|
const held = readLock(path);
|
|
25876
25487
|
if (held && held.pid !== process.pid && processAlive(held.pid))
|
|
25877
25488
|
return null;
|
|
25878
|
-
|
|
25489
|
+
mkdirSync18(stateDir(home), { recursive: true });
|
|
25879
25490
|
const body = { pid: process.pid, startedAt: Date.now() };
|
|
25880
|
-
|
|
25491
|
+
writeFileSync16(path, JSON.stringify(body), "utf8");
|
|
25881
25492
|
let released = false;
|
|
25882
25493
|
return {
|
|
25883
25494
|
release() {
|
|
@@ -25887,7 +25498,7 @@ function acquireOnboardingLock(home = homedir26()) {
|
|
|
25887
25498
|
try {
|
|
25888
25499
|
const current = readLock(path);
|
|
25889
25500
|
if (!current || current.pid === process.pid)
|
|
25890
|
-
|
|
25501
|
+
rmSync11(path, { force: true });
|
|
25891
25502
|
} catch {}
|
|
25892
25503
|
}
|
|
25893
25504
|
};
|
|
@@ -25900,7 +25511,7 @@ var init_onboarding_lock = __esm(() => {
|
|
|
25900
25511
|
});
|
|
25901
25512
|
|
|
25902
25513
|
// lib/claude-sessions.ts
|
|
25903
|
-
import { readdirSync as
|
|
25514
|
+
import { readdirSync as readdirSync16, statSync as statSync18, existsSync as existsSync29 } from "node:fs";
|
|
25904
25515
|
import { join as join32, basename as basename6 } from "node:path";
|
|
25905
25516
|
function getClaudeProjectsRoot() {
|
|
25906
25517
|
return getClaudeProjectsPath();
|
|
@@ -25909,7 +25520,7 @@ function listClaudeProjects() {
|
|
|
25909
25520
|
const root = getClaudeProjectsRoot();
|
|
25910
25521
|
let entries;
|
|
25911
25522
|
try {
|
|
25912
|
-
entries =
|
|
25523
|
+
entries = readdirSync16(root, { withFileTypes: true });
|
|
25913
25524
|
} catch {
|
|
25914
25525
|
return [];
|
|
25915
25526
|
}
|
|
@@ -25919,45 +25530,11 @@ function listClaudeProjects() {
|
|
|
25919
25530
|
path: join32(root, e.name)
|
|
25920
25531
|
}));
|
|
25921
25532
|
}
|
|
25922
|
-
function collectSubagentTranscripts(dir, relPrefix, project, out, depth) {
|
|
25923
|
-
if (depth > MAX_SUBAGENT_DEPTH)
|
|
25924
|
-
return;
|
|
25925
|
-
let entries;
|
|
25926
|
-
try {
|
|
25927
|
-
entries = readdirSync17(dir, { withFileTypes: true });
|
|
25928
|
-
} catch {
|
|
25929
|
-
return;
|
|
25930
|
-
}
|
|
25931
|
-
for (const entry of entries) {
|
|
25932
|
-
if (entry.isSymbolicLink())
|
|
25933
|
-
continue;
|
|
25934
|
-
if (entry.isDirectory()) {
|
|
25935
|
-
collectSubagentTranscripts(join32(dir, entry.name), relPrefix ? `${relPrefix}__${entry.name}` : entry.name, project, out, depth + 1);
|
|
25936
|
-
continue;
|
|
25937
|
-
}
|
|
25938
|
-
if (!entry.isFile() || !entry.name.endsWith(".jsonl"))
|
|
25939
|
-
continue;
|
|
25940
|
-
const stem = entry.name.slice(0, -".jsonl".length);
|
|
25941
|
-
const transcriptPath = join32(dir, entry.name);
|
|
25942
|
-
try {
|
|
25943
|
-
const s = statSync18(transcriptPath);
|
|
25944
|
-
out.push({
|
|
25945
|
-
projectName: project.name,
|
|
25946
|
-
cwd: project.cwd,
|
|
25947
|
-
sessionId: relPrefix ? `${relPrefix}__${stem}` : stem,
|
|
25948
|
-
transcriptPath,
|
|
25949
|
-
mtimeMs: s.mtimeMs,
|
|
25950
|
-
sizeBytes: s.size,
|
|
25951
|
-
isSubagent: true
|
|
25952
|
-
});
|
|
25953
|
-
} catch {}
|
|
25954
|
-
}
|
|
25955
|
-
}
|
|
25956
25533
|
function listClaudeTranscripts(project) {
|
|
25957
25534
|
const out = [];
|
|
25958
25535
|
let entries;
|
|
25959
25536
|
try {
|
|
25960
|
-
entries =
|
|
25537
|
+
entries = readdirSync16(project.path, { withFileTypes: true });
|
|
25961
25538
|
} catch {
|
|
25962
25539
|
return out;
|
|
25963
25540
|
}
|
|
@@ -25981,14 +25558,37 @@ function listClaudeTranscripts(project) {
|
|
|
25981
25558
|
} catch {}
|
|
25982
25559
|
} else if (entry.isDirectory() && UUID_RE2.test(entry.name)) {
|
|
25983
25560
|
const subDir = join32(project.path, entry.name, "subagents");
|
|
25984
|
-
if (!
|
|
25561
|
+
if (!existsSync29(subDir))
|
|
25985
25562
|
continue;
|
|
25986
|
-
|
|
25563
|
+
let subEntries;
|
|
25564
|
+
try {
|
|
25565
|
+
subEntries = readdirSync16(subDir, { withFileTypes: true });
|
|
25566
|
+
} catch {
|
|
25567
|
+
continue;
|
|
25568
|
+
}
|
|
25569
|
+
for (const sub of subEntries) {
|
|
25570
|
+
if (!sub.isFile() || !sub.name.endsWith(".jsonl"))
|
|
25571
|
+
continue;
|
|
25572
|
+
const agentId = sub.name.slice(0, -".jsonl".length);
|
|
25573
|
+
const transcriptPath = join32(subDir, sub.name);
|
|
25574
|
+
try {
|
|
25575
|
+
const s = statSync18(transcriptPath);
|
|
25576
|
+
out.push({
|
|
25577
|
+
projectName: project.name,
|
|
25578
|
+
cwd: project.cwd,
|
|
25579
|
+
sessionId: agentId,
|
|
25580
|
+
transcriptPath,
|
|
25581
|
+
mtimeMs: s.mtimeMs,
|
|
25582
|
+
sizeBytes: s.size,
|
|
25583
|
+
isSubagent: true
|
|
25584
|
+
});
|
|
25585
|
+
} catch {}
|
|
25586
|
+
}
|
|
25987
25587
|
}
|
|
25988
25588
|
}
|
|
25989
25589
|
return out;
|
|
25990
25590
|
}
|
|
25991
|
-
var UUID_RE2
|
|
25591
|
+
var UUID_RE2;
|
|
25992
25592
|
var init_claude_sessions = __esm(() => {
|
|
25993
25593
|
init_paths();
|
|
25994
25594
|
UUID_RE2 = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i;
|
|
@@ -26047,7 +25647,6 @@ var init_shared = __esm(() => {
|
|
|
26047
25647
|
|
|
26048
25648
|
// src/audit/cli-adapters/claude.ts
|
|
26049
25649
|
import { readFile as readFile15, open as open2 } from "node:fs/promises";
|
|
26050
|
-
import { readFileSync as readFileSync25 } from "node:fs";
|
|
26051
25650
|
async function listClaudeTranscriptMetadata(opts = {}) {
|
|
26052
25651
|
const projectFilter = opts.projects ? new Set(opts.projects) : null;
|
|
26053
25652
|
const sinceMs = opts.sinceMs ?? 0;
|
|
@@ -26064,22 +25663,13 @@ async function listClaudeTranscriptMetadata(opts = {}) {
|
|
|
26064
25663
|
for (const t of transcripts) {
|
|
26065
25664
|
if (t.mtimeMs < sinceMs)
|
|
26066
25665
|
continue;
|
|
26067
|
-
let sessionDescription;
|
|
26068
|
-
if (t.isSubagent) {
|
|
26069
|
-
try {
|
|
26070
|
-
const meta = JSON.parse(readFileSync25(t.transcriptPath.replace(/\.jsonl$/, ".meta.json"), "utf8"));
|
|
26071
|
-
if (typeof meta.description === "string")
|
|
26072
|
-
sessionDescription = meta.description;
|
|
26073
|
-
} catch {}
|
|
26074
|
-
}
|
|
26075
25666
|
out.push({
|
|
26076
25667
|
cli: "claude",
|
|
26077
25668
|
projectName: project.name,
|
|
26078
25669
|
sessionId: t.sessionId,
|
|
26079
25670
|
transcriptPath: t.transcriptPath,
|
|
26080
25671
|
mtimeMs: t.mtimeMs,
|
|
26081
|
-
sizeBytes: t.sizeBytes
|
|
26082
|
-
sessionDescription
|
|
25672
|
+
sizeBytes: t.sizeBytes
|
|
26083
25673
|
});
|
|
26084
25674
|
}
|
|
26085
25675
|
}
|
|
@@ -27309,420 +26899,9 @@ var init_features = __esm(() => {
|
|
|
27309
26899
|
})();
|
|
27310
26900
|
});
|
|
27311
26901
|
|
|
27312
|
-
// src/audit/leak-scan.ts
|
|
27313
|
-
function unquote(v) {
|
|
27314
|
-
return v.startsWith('"') && v.endsWith('"') || v.startsWith("'") && v.endsWith("'") ? v.slice(1, -1) : v;
|
|
27315
|
-
}
|
|
27316
|
-
function isDocsLiteral(value) {
|
|
27317
|
-
const { createHash } = __require("node:crypto");
|
|
27318
|
-
return DOCS_LITERALS.has(createHash("sha256").update(value).digest("hex"));
|
|
27319
|
-
}
|
|
27320
|
-
function isSelfOutput(text) {
|
|
27321
|
-
return SELF_OUTPUT_MARKERS.some((m) => text.includes(m));
|
|
27322
|
-
}
|
|
27323
|
-
function literalPrefixOf(source) {
|
|
27324
|
-
let out = "";
|
|
27325
|
-
for (let i = 0;i < source.length; i++) {
|
|
27326
|
-
const c = source[i];
|
|
27327
|
-
if (c === "\\") {
|
|
27328
|
-
const next = source[i + 1];
|
|
27329
|
-
if (next === undefined)
|
|
27330
|
-
break;
|
|
27331
|
-
if (/[dwsSDWbB]/.test(next))
|
|
27332
|
-
break;
|
|
27333
|
-
out += next;
|
|
27334
|
-
i++;
|
|
27335
|
-
continue;
|
|
27336
|
-
}
|
|
27337
|
-
if ("[](){}|?*+^$.".includes(c))
|
|
27338
|
-
break;
|
|
27339
|
-
out += c;
|
|
27340
|
-
}
|
|
27341
|
-
return out.length >= 2 ? out : null;
|
|
27342
|
-
}
|
|
27343
|
-
function applicablePatterns(text) {
|
|
27344
|
-
return GATED_PATTERNS.filter(([, , gate]) => gate === null || text.includes(gate));
|
|
27345
|
-
}
|
|
27346
|
-
function findSecrets(text) {
|
|
27347
|
-
if (!text)
|
|
27348
|
-
return [];
|
|
27349
|
-
if (isSelfOutput(text))
|
|
27350
|
-
return [];
|
|
27351
|
-
const byValue = new Map;
|
|
27352
|
-
for (const [pattern, label] of applicablePatterns(text)) {
|
|
27353
|
-
const re = new RegExp(pattern.source, pattern.flags.includes("g") ? pattern.flags : pattern.flags + "g");
|
|
27354
|
-
for (const m of text.matchAll(re)) {
|
|
27355
|
-
const value = m[0];
|
|
27356
|
-
if (isDocsLiteral(value))
|
|
27357
|
-
continue;
|
|
27358
|
-
if (!byValue.has(value))
|
|
27359
|
-
byValue.set(value, { value, name: null, rule: label, shaped: true });
|
|
27360
|
-
}
|
|
27361
|
-
}
|
|
27362
|
-
for (const m of text.matchAll(ASSIGNMENT_RE)) {
|
|
27363
|
-
const name = m[2];
|
|
27364
|
-
const value = unquote(m[4]);
|
|
27365
|
-
const existing = byValue.get(value);
|
|
27366
|
-
if (existing)
|
|
27367
|
-
existing.name ??= name;
|
|
27368
|
-
}
|
|
27369
|
-
return [...byValue.values()];
|
|
27370
|
-
}
|
|
27371
|
-
function flattenToolInput(input, depth = 0) {
|
|
27372
|
-
if (depth > 6 || input == null)
|
|
27373
|
-
return "";
|
|
27374
|
-
if (typeof input === "string")
|
|
27375
|
-
return input;
|
|
27376
|
-
if (typeof input === "number" || typeof input === "boolean")
|
|
27377
|
-
return String(input);
|
|
27378
|
-
if (Array.isArray(input))
|
|
27379
|
-
return input.map((v) => flattenToolInput(v, depth + 1)).join(`
|
|
27380
|
-
`);
|
|
27381
|
-
if (typeof input === "object") {
|
|
27382
|
-
return Object.entries(input).map(([k, v]) => `${k}=${flattenToolInput(v, depth + 1)}`).join(`
|
|
27383
|
-
`);
|
|
27384
|
-
}
|
|
27385
|
-
return "";
|
|
27386
|
-
}
|
|
27387
|
-
var LEAK_SCAN_VERSION = 6, ASSIGNMENT_RE, DOCS_LITERALS, SELF_OUTPUT_MARKERS, GATED_PATTERNS;
|
|
27388
|
-
var init_leak_scan = __esm(() => {
|
|
27389
|
-
init_builtin_policies();
|
|
27390
|
-
ASSIGNMENT_RE = /(["']?)\b([A-Za-z_][A-Za-z0-9_]{0,127})\1([ \t]*(?::(?!\/\/)|=)[ \t]*)("[^"]*"|'[^']*'|[^\s;|&"']+)/g;
|
|
27391
|
-
DOCS_LITERALS = new Set([
|
|
27392
|
-
"1a5d44a2dca19669d72edf4c4f1c27c4c1ca4b4408fbb17f6ce4ad452d78ddb3",
|
|
27393
|
-
"78314b11be2e581549ac1c4f616563fad3fdf0c3b71678f6e2299182080e0598",
|
|
27394
|
-
"cb20d34a7adda2f5d2043836051e242fd4c2b233e02c80c91fe9d5af9b100574",
|
|
27395
|
-
"3543c50b0cb9cea8e55eb1f529b79f043cb250d485fde7b66cabbb5183add7da",
|
|
27396
|
-
"1fae28c1b317300acfd90078c973567c8159015822b0c8b81508a5ec40517d20",
|
|
27397
|
-
"23219833092a3136d7ad178ccb62ebd6ac8cba54ff24c2cfcd37b84aba9e95cf",
|
|
27398
|
-
"872db7f4419592e397ef45230f910e06b48c36ed9970cf1f9e4fb896e69f129c",
|
|
27399
|
-
"78a08441f4314f0a2833cfe58c62e555a162264206982cda39f6804f5048f570",
|
|
27400
|
-
"2cafc0970149a84f3b9e62eaf169f36f59907a3b3e31f7b82e68c69cd27f7326",
|
|
27401
|
-
"b363ade84d56c43bebbc9339b4971e340e3f13f7f8d45580b53373d2763e9e33"
|
|
27402
|
-
]);
|
|
27403
|
-
SELF_OUTPUT_MARKERS = [
|
|
27404
|
-
"[REDACTED:",
|
|
27405
|
-
"[redacted:",
|
|
27406
|
-
"failproofai audit",
|
|
27407
|
-
"assigned secret]"
|
|
27408
|
-
];
|
|
27409
|
-
GATED_PATTERNS = SECRET_PATTERNS.map(([re, label]) => [re, label, literalPrefixOf(re.source)]);
|
|
27410
|
-
});
|
|
27411
|
-
|
|
27412
|
-
// src/audit/leak-record.ts
|
|
27413
|
-
function upsertFinding(record, incoming) {
|
|
27414
|
-
const { sighting, ...rest } = incoming;
|
|
27415
|
-
const existing = record.findings.find((f) => f.id === rest.id);
|
|
27416
|
-
if (!existing) {
|
|
27417
|
-
record.findings.push({
|
|
27418
|
-
...rest,
|
|
27419
|
-
firstSeen: sighting.at,
|
|
27420
|
-
lastSeen: sighting.at,
|
|
27421
|
-
occurrences: 1,
|
|
27422
|
-
sightings: [sighting]
|
|
27423
|
-
});
|
|
27424
|
-
return { record, isNew: true };
|
|
27425
|
-
}
|
|
27426
|
-
const seen = existing.sightings.some((s) => s.sessionId === sighting.sessionId && s.at === sighting.at && s.mechanism.direction === sighting.mechanism.direction && s.mechanism.summary === sighting.mechanism.summary);
|
|
27427
|
-
if (seen)
|
|
27428
|
-
return { record, isNew: false };
|
|
27429
|
-
existing.occurrences += 1;
|
|
27430
|
-
if (sighting.at > existing.lastSeen)
|
|
27431
|
-
existing.lastSeen = sighting.at;
|
|
27432
|
-
if (sighting.at < existing.firstSeen)
|
|
27433
|
-
existing.firstSeen = sighting.at;
|
|
27434
|
-
existing.sightings.push(sighting);
|
|
27435
|
-
existing.sightings.sort((a, b) => a.at.localeCompare(b.at));
|
|
27436
|
-
if (existing.sightings.length > MAX_SIGHTINGS) {
|
|
27437
|
-
existing.sightings.splice(1, existing.sightings.length - MAX_SIGHTINGS);
|
|
27438
|
-
}
|
|
27439
|
-
return { record, isNew: false };
|
|
27440
|
-
}
|
|
27441
|
-
function pruneRecord(record, nowMs) {
|
|
27442
|
-
const cutoff = new Date(nowMs - FINDING_TTL_DAYS * 86400000).toISOString();
|
|
27443
|
-
const live = record.findings.filter((f) => !f.lastSeen || !Number.isFinite(Date.parse(f.lastSeen)) || f.lastSeen >= cutoff);
|
|
27444
|
-
live.sort((a, b) => a.lastSeen < b.lastSeen ? 1 : a.lastSeen > b.lastSeen ? -1 : 0);
|
|
27445
|
-
record.findings = live.slice(0, MAX_FINDINGS);
|
|
27446
|
-
return record;
|
|
27447
|
-
}
|
|
27448
|
-
function emptyRecord(salt, nowIso) {
|
|
27449
|
-
return {
|
|
27450
|
-
schemaVersion: LEAK_RECORD_SCHEMA_VERSION,
|
|
27451
|
-
salt,
|
|
27452
|
-
updatedAt: nowIso,
|
|
27453
|
-
findings: []
|
|
27454
|
-
};
|
|
27455
|
-
}
|
|
27456
|
-
function describeMechanism(toolName, direction, shortPath) {
|
|
27457
|
-
const where = shortPath ? ` ${shortPath}` : "";
|
|
27458
|
-
let summary;
|
|
27459
|
-
if (direction === "result") {
|
|
27460
|
-
summary = shortPath ? `appeared in the output of reading${where}` : "appeared in tool output";
|
|
27461
|
-
} else {
|
|
27462
|
-
switch (toolName) {
|
|
27463
|
-
case "Read":
|
|
27464
|
-
summary = `read from${where || " a file"}`;
|
|
27465
|
-
break;
|
|
27466
|
-
case "Write":
|
|
27467
|
-
summary = `written to${where || " a file"}`;
|
|
27468
|
-
break;
|
|
27469
|
-
case "Edit":
|
|
27470
|
-
case "NotebookEdit":
|
|
27471
|
-
summary = `edited into${where || " a file"}`;
|
|
27472
|
-
break;
|
|
27473
|
-
case "Bash":
|
|
27474
|
-
summary = "passed in a shell command";
|
|
27475
|
-
break;
|
|
27476
|
-
default:
|
|
27477
|
-
summary = `sent to the ${toolName} tool`;
|
|
27478
|
-
}
|
|
27479
|
-
}
|
|
27480
|
-
return { summary, toolName, direction };
|
|
27481
|
-
}
|
|
27482
|
-
var MAX_SIGHTINGS = 5, FINDING_TTL_DAYS = 90, MAX_FINDINGS = 500, LEAK_RECORD_SCHEMA_VERSION = 1;
|
|
27483
|
-
|
|
27484
|
-
// src/audit/leak-store.ts
|
|
27485
|
-
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
27486
|
-
import { existsSync as existsSync31, mkdirSync as mkdirSync20, readFileSync as readFileSync26 } from "node:fs";
|
|
27487
|
-
import { dirname as dirname16 } from "node:path";
|
|
27488
|
-
function readJson(path) {
|
|
27489
|
-
try {
|
|
27490
|
-
if (!existsSync31(path))
|
|
27491
|
-
return null;
|
|
27492
|
-
return JSON.parse(readFileSync26(path, "utf8"));
|
|
27493
|
-
} catch {
|
|
27494
|
-
return null;
|
|
27495
|
-
}
|
|
27496
|
-
}
|
|
27497
|
-
function readLeakIdentity(home) {
|
|
27498
|
-
const existing = readJson(auditLeakIdentityFile(home));
|
|
27499
|
-
if (existing && typeof existing.salt === "string" && existing.salt.length >= 32) {
|
|
27500
|
-
return {
|
|
27501
|
-
salt: existing.salt,
|
|
27502
|
-
dismissed: existing.dismissed ?? []
|
|
27503
|
-
};
|
|
27504
|
-
}
|
|
27505
|
-
const minted = { salt: randomBytes2(32).toString("hex"), dismissed: [] };
|
|
27506
|
-
writeLeakIdentity(minted, home);
|
|
27507
|
-
return minted;
|
|
27508
|
-
}
|
|
27509
|
-
function writeLeakIdentity(identity, home) {
|
|
27510
|
-
const path = auditLeakIdentityFile(home);
|
|
27511
|
-
try {
|
|
27512
|
-
mkdirSync20(dirname16(path), { recursive: true });
|
|
27513
|
-
writeJsonAtomically(path, identity, { mode: FILE_MODE });
|
|
27514
|
-
return true;
|
|
27515
|
-
} catch {
|
|
27516
|
-
return false;
|
|
27517
|
-
}
|
|
27518
|
-
}
|
|
27519
|
-
function sanitizeFinding(raw) {
|
|
27520
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
27521
|
-
return null;
|
|
27522
|
-
const f = raw;
|
|
27523
|
-
if (typeof f.id !== "string" || !isFindingId(f.id))
|
|
27524
|
-
return null;
|
|
27525
|
-
const fp = f.fingerprint;
|
|
27526
|
-
if (!fp || typeof fp !== "object" || typeof fp.display !== "string" || typeof fp.label !== "string") {
|
|
27527
|
-
return null;
|
|
27528
|
-
}
|
|
27529
|
-
const sightings = Array.isArray(f.sightings) ? f.sightings.filter((s) => {
|
|
27530
|
-
if (!s || typeof s !== "object")
|
|
27531
|
-
return false;
|
|
27532
|
-
const m = s.mechanism;
|
|
27533
|
-
return !!m && typeof m === "object" && typeof m.summary === "string";
|
|
27534
|
-
}) : [];
|
|
27535
|
-
return {
|
|
27536
|
-
...f,
|
|
27537
|
-
fingerprint: {
|
|
27538
|
-
display: fp.display,
|
|
27539
|
-
label: fp.label,
|
|
27540
|
-
length: typeof fp.length === "number" && Number.isFinite(fp.length) ? fp.length : 0,
|
|
27541
|
-
attributed: fp.attributed === true
|
|
27542
|
-
},
|
|
27543
|
-
name: typeof f.name === "string" ? f.name : null,
|
|
27544
|
-
firstSeen: typeof f.firstSeen === "string" ? f.firstSeen : "",
|
|
27545
|
-
lastSeen: typeof f.lastSeen === "string" ? f.lastSeen : "",
|
|
27546
|
-
occurrences: typeof f.occurrences === "number" && Number.isFinite(f.occurrences) ? f.occurrences : sightings.length,
|
|
27547
|
-
sightings
|
|
27548
|
-
};
|
|
27549
|
-
}
|
|
27550
|
-
function readLeakRecord(home) {
|
|
27551
|
-
const identity = readLeakIdentity(home);
|
|
27552
|
-
const raw = readJson(auditLeaksFile(home));
|
|
27553
|
-
if (!raw || raw.schemaVersion !== LEAK_RECORD_SCHEMA_VERSION || !Array.isArray(raw.findings)) {
|
|
27554
|
-
return emptyRecord(identity.salt, new Date().toISOString());
|
|
27555
|
-
}
|
|
27556
|
-
const findings = raw.findings.map(sanitizeFinding).filter((f) => f !== null);
|
|
27557
|
-
return { ...raw, findings, salt: identity.salt };
|
|
27558
|
-
}
|
|
27559
|
-
function writeLeakRecord(record, home, nowMs = Date.now()) {
|
|
27560
|
-
const path = auditLeaksFile(home);
|
|
27561
|
-
try {
|
|
27562
|
-
mkdirSync20(dirname16(path), { recursive: true });
|
|
27563
|
-
const pruned = pruneRecord(record, nowMs);
|
|
27564
|
-
pruned.updatedAt = new Date(nowMs).toISOString();
|
|
27565
|
-
writeJsonAtomically(path, pruned, { mode: FILE_MODE });
|
|
27566
|
-
return true;
|
|
27567
|
-
} catch {
|
|
27568
|
-
return false;
|
|
27569
|
-
}
|
|
27570
|
-
}
|
|
27571
|
-
function activeFindings(record, home) {
|
|
27572
|
-
const dismissed = new Set(readLeakIdentity(home).dismissed);
|
|
27573
|
-
return record.findings.filter((f) => !dismissed.has(f.id)).sort((a, b) => a.lastSeen < b.lastSeen ? 1 : a.lastSeen > b.lastSeen ? -1 : 0);
|
|
27574
|
-
}
|
|
27575
|
-
var FILE_MODE = 384;
|
|
27576
|
-
var init_leak_store = __esm(() => {
|
|
27577
|
-
init_fp_home();
|
|
27578
|
-
init_atomic_write();
|
|
27579
|
-
init_leak_fingerprint();
|
|
27580
|
-
});
|
|
27581
|
-
|
|
27582
|
-
// src/audit/redact-example.ts
|
|
27583
|
-
import { homedir as homedir27 } from "node:os";
|
|
27584
|
-
function maskTruncatedSecret(input) {
|
|
27585
|
-
for (const [pattern, label] of SECRET_PREFIXES) {
|
|
27586
|
-
if (pattern.test(input)) {
|
|
27587
|
-
return input.replace(pattern, `[REDACTED: ${label}]`);
|
|
27588
|
-
}
|
|
27589
|
-
}
|
|
27590
|
-
return input;
|
|
27591
|
-
}
|
|
27592
|
-
function identifierComponents(name) {
|
|
27593
|
-
return name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").toUpperCase().split(/[_\-.]+/).filter(Boolean);
|
|
27594
|
-
}
|
|
27595
|
-
function isPublishedByDesign(name) {
|
|
27596
|
-
const components = identifierComponents(name);
|
|
27597
|
-
if (components.length === 0)
|
|
27598
|
-
return false;
|
|
27599
|
-
const upper = components.join("_");
|
|
27600
|
-
for (const prefix of CLIENT_BUNDLE_PREFIXES) {
|
|
27601
|
-
if (upper === prefix || upper.startsWith(prefix + "_"))
|
|
27602
|
-
return true;
|
|
27603
|
-
}
|
|
27604
|
-
return PUBLISHED_MARKERS.test(upper);
|
|
27605
|
-
}
|
|
27606
|
-
function isSecretName(name) {
|
|
27607
|
-
if (isPublishedByDesign(name))
|
|
27608
|
-
return false;
|
|
27609
|
-
const upper = name.toUpperCase();
|
|
27610
|
-
if (SECRET_NAME_SUBSTRINGS.some((word) => upper.includes(word)))
|
|
27611
|
-
return true;
|
|
27612
|
-
const components = identifierComponents(name);
|
|
27613
|
-
if (components.some((part) => SECRET_NAME_COMPONENTS.includes(part)))
|
|
27614
|
-
return true;
|
|
27615
|
-
return components.length > 1 && components.some((part) => COMPOUND_ONLY_COMPONENTS.includes(part));
|
|
27616
|
-
}
|
|
27617
|
-
function maskAssignedSecrets(input) {
|
|
27618
|
-
let out = input.replace(ASSIGNMENT_RE2, (match, quote, name, separator, value) => {
|
|
27619
|
-
if (!isSecretName(name))
|
|
27620
|
-
return match;
|
|
27621
|
-
if (value.startsWith("[REDACTED"))
|
|
27622
|
-
return match;
|
|
27623
|
-
return `${quote}${name}${quote}${separator}[REDACTED: assigned secret]`;
|
|
27624
|
-
});
|
|
27625
|
-
out = out.replace(URL_CREDENTIALS_RE, "$1[REDACTED: URL credentials]@");
|
|
27626
|
-
out = out.replace(BASIC_AUTH_FLAG_RE, "$1[REDACTED: basic auth]");
|
|
27627
|
-
return out;
|
|
27628
|
-
}
|
|
27629
|
-
function maskSecrets(input) {
|
|
27630
|
-
let out = input;
|
|
27631
|
-
for (const [pattern, label] of SECRET_PATTERNS) {
|
|
27632
|
-
const global = new RegExp(pattern.source, pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`);
|
|
27633
|
-
out = out.replace(global, `[REDACTED: ${label}]`);
|
|
27634
|
-
}
|
|
27635
|
-
return out;
|
|
27636
|
-
}
|
|
27637
|
-
function shortenPaths(input, home = homedir27()) {
|
|
27638
|
-
const homeRoot = home.replace(/\/+$/, "");
|
|
27639
|
-
return input.replace(ABSOLUTE_PATH_RE, (match, offset, whole) => {
|
|
27640
|
-
if (PUBLIC_PATH_ROOTS.some((root) => match.startsWith(root)))
|
|
27641
|
-
return match;
|
|
27642
|
-
if (offset > 0 && whole[offset - 1] === "/" && /[a-z][a-z0-9+.\-]*:$/i.test(whole.slice(0, offset - 1))) {
|
|
27643
|
-
const urlSegments = match.split("/").filter(Boolean);
|
|
27644
|
-
if (urlSegments.length <= 1)
|
|
27645
|
-
return match;
|
|
27646
|
-
const host = urlSegments[0];
|
|
27647
|
-
const leaf = urlSegments[urlSegments.length - 1];
|
|
27648
|
-
const elided = urlSegments.length > 2 ? "/…" : "";
|
|
27649
|
-
return `/${host}${elided}/${leaf}${match.endsWith("/") ? "/" : ""}`;
|
|
27650
|
-
}
|
|
27651
|
-
const trailingSlash = match.endsWith("/");
|
|
27652
|
-
const segments = match.split("/").filter(Boolean);
|
|
27653
|
-
if (segments.length === 0)
|
|
27654
|
-
return match;
|
|
27655
|
-
const basename = segments[segments.length - 1];
|
|
27656
|
-
const kept = segments.slice(Math.max(0, segments.length - 1 - KEPT_PARENT_SEGMENTS), segments.length - 1);
|
|
27657
|
-
const matchRoot = match.replace(/\/+$/, "");
|
|
27658
|
-
const underHome = homeRoot.length > 0 && (matchRoot === homeRoot || matchRoot.startsWith(`${homeRoot}/`));
|
|
27659
|
-
if (matchRoot === homeRoot && homeRoot.length > 0) {
|
|
27660
|
-
return trailingSlash ? "~/" : "~";
|
|
27661
|
-
}
|
|
27662
|
-
const root = underHome ? "~" : "";
|
|
27663
|
-
const middle = segments.length - kept.length - 1 > 0 ? "/…" : "";
|
|
27664
|
-
const tail = [...kept, basename].join("/");
|
|
27665
|
-
return `${root}${middle}/${tail}${trailingSlash ? "/" : ""}`;
|
|
27666
|
-
});
|
|
27667
|
-
}
|
|
27668
|
-
function maskSecretsOnly(input) {
|
|
27669
|
-
return maskAssignedSecrets(maskTruncatedSecret(maskSecrets(input)));
|
|
27670
|
-
}
|
|
27671
|
-
function redactExample(input, home = homedir27()) {
|
|
27672
|
-
const masked = maskSecretsOnly(input);
|
|
27673
|
-
const shortened = shortenPaths(masked, home);
|
|
27674
|
-
const collapsed = shortened.replace(/\s+/g, " ").trim();
|
|
27675
|
-
return collapsed.length > REDACTED_EXAMPLE_MAX_CHARS ? `${collapsed.slice(0, REDACTED_EXAMPLE_MAX_CHARS - 1)}…` : collapsed;
|
|
27676
|
-
}
|
|
27677
|
-
var REDACTED_EXAMPLE_MAX_CHARS = 160, KEPT_PARENT_SEGMENTS = 0, ABSOLUTE_PATH_RE, PUBLIC_PATH_ROOTS, SECRET_PREFIXES, SECRET_NAME_SUBSTRINGS, SECRET_NAME_COMPONENTS, COMPOUND_ONLY_COMPONENTS, ASSIGNMENT_RE2, URL_CREDENTIALS_RE, BASIC_AUTH_FLAG_RE, CLIENT_BUNDLE_PREFIXES, PUBLISHED_MARKERS;
|
|
27678
|
-
var init_redact_example = __esm(() => {
|
|
27679
|
-
init_builtin_policies();
|
|
27680
|
-
ABSOLUTE_PATH_RE = /(?:\/[\w.\-@+]+){2,}\/?/g;
|
|
27681
|
-
PUBLIC_PATH_ROOTS = ["/dev/", "/proc/", "/sys/"];
|
|
27682
|
-
SECRET_PREFIXES = [
|
|
27683
|
-
[/(?<!\w)(?:Authorization:\s*)?Bearer\s+\S*$/i, "bearer token"],
|
|
27684
|
-
[/(?<!\w)sk-ant-\S*$/, "Anthropic API key"],
|
|
27685
|
-
[/(?<!\w)sk-proj-\S*$/, "OpenAI project API key"],
|
|
27686
|
-
[/(?<!\w)sk-\S*$/, "OpenAI API key"],
|
|
27687
|
-
[/(?<!\w)ghp_\S*$/, "GitHub personal access token"],
|
|
27688
|
-
[/(?<!\w)github_pat_\S*$/, "GitHub fine-grained token"],
|
|
27689
|
-
[/(?<!\w)AKIA\S*$/, "AWS access key ID"],
|
|
27690
|
-
[/(?<!\w)sk_live_\S*$/, "Stripe live secret key"],
|
|
27691
|
-
[/(?<!\w)sk_test_\S*$/, "Stripe test secret key"],
|
|
27692
|
-
[/(?<!\w)AIza\S*$/, "Google API key"],
|
|
27693
|
-
[/-----BEGIN\s[A-Z ]*$/, "private key"]
|
|
27694
|
-
];
|
|
27695
|
-
SECRET_NAME_SUBSTRINGS = [
|
|
27696
|
-
"TOKEN",
|
|
27697
|
-
"SECRET",
|
|
27698
|
-
"PASSWORD",
|
|
27699
|
-
"PASSWD",
|
|
27700
|
-
"PASSPHRASE",
|
|
27701
|
-
"CREDENTIAL",
|
|
27702
|
-
"APIKEY",
|
|
27703
|
-
"PRIVATEKEY"
|
|
27704
|
-
];
|
|
27705
|
-
SECRET_NAME_COMPONENTS = ["PASS", "AUTH", "PAT", "SIG", "SIGNATURE", "COOKIE"];
|
|
27706
|
-
COMPOUND_ONLY_COMPONENTS = ["PWD", "KEY"];
|
|
27707
|
-
ASSIGNMENT_RE2 = /(["']?)\b([A-Za-z_][A-Za-z0-9_]{0,127})\1([ \t]*(?::(?!\/\/)|=)[ \t]*)("[^"]*"|'[^']*'|[^\s;|&"']+)/g;
|
|
27708
|
-
URL_CREDENTIALS_RE = /([a-z][a-z0-9+.\-]{0,31}:\/\/)[^\s/:@]{1,256}:[^\s/@]{1,256}@/gi;
|
|
27709
|
-
BASIC_AUTH_FLAG_RE = /((?:^|\s)(?:-u|--user)[\s=])\S+:\S+/g;
|
|
27710
|
-
CLIENT_BUNDLE_PREFIXES = [
|
|
27711
|
-
"NEXT_PUBLIC",
|
|
27712
|
-
"VITE",
|
|
27713
|
-
"REACT_APP",
|
|
27714
|
-
"EXPO_PUBLIC",
|
|
27715
|
-
"NUXT_PUBLIC",
|
|
27716
|
-
"GATSBY",
|
|
27717
|
-
"STORYBOOK",
|
|
27718
|
-
"PUBLIC"
|
|
27719
|
-
];
|
|
27720
|
-
PUBLISHED_MARKERS = /PUBLISHABLE|(^|_)PUBLIC_KEY$|(^|_)ANON_KEY$/;
|
|
27721
|
-
});
|
|
27722
|
-
|
|
27723
26902
|
// src/audit/cache.ts
|
|
27724
26903
|
import { createHash as createHash8 } from "node:crypto";
|
|
27725
|
-
import { existsSync as
|
|
26904
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync19, readFileSync as readFileSync25, writeFileSync as writeFileSync17, chmodSync as chmodSync5, openSync, readSync, closeSync } from "node:fs";
|
|
27726
26905
|
import { join as join33 } from "node:path";
|
|
27727
26906
|
function getEngineVersion() {
|
|
27728
26907
|
if (cachedEngineVersion)
|
|
@@ -27783,10 +26962,10 @@ function readCachedTranscript(transcriptPath, mtimeMs, sizeBytes) {
|
|
|
27783
26962
|
if (sizeBytes === 0)
|
|
27784
26963
|
return null;
|
|
27785
26964
|
const cachePath = getCachePathFor(transcriptPath);
|
|
27786
|
-
if (!
|
|
26965
|
+
if (!existsSync30(cachePath))
|
|
27787
26966
|
return null;
|
|
27788
26967
|
try {
|
|
27789
|
-
const raw =
|
|
26968
|
+
const raw = readFileSync25(cachePath, "utf-8");
|
|
27790
26969
|
const entry = JSON.parse(raw);
|
|
27791
26970
|
if (entry.schemaVersion !== CACHE_SCHEMA_VERSION)
|
|
27792
26971
|
return null;
|
|
@@ -27794,8 +26973,6 @@ function readCachedTranscript(transcriptPath, mtimeMs, sizeBytes) {
|
|
|
27794
26973
|
return null;
|
|
27795
26974
|
if (entry.detectorVersion !== getDetectorVersion())
|
|
27796
26975
|
return null;
|
|
27797
|
-
if (entry.leakScanVersion !== LEAK_SCAN_VERSION)
|
|
27798
|
-
return null;
|
|
27799
26976
|
if (!Number.isFinite(entry.cachedAt) || Date.now() - entry.cachedAt > CACHE_TTL_MS)
|
|
27800
26977
|
return null;
|
|
27801
26978
|
if (!entry.result)
|
|
@@ -27827,7 +27004,7 @@ function writeCachedTranscriptResult(transcriptPath, mtimeMs, sizeBytes, result,
|
|
|
27827
27004
|
return;
|
|
27828
27005
|
const cachePath = getCachePathFor(transcriptPath);
|
|
27829
27006
|
try {
|
|
27830
|
-
|
|
27007
|
+
mkdirSync19(auditCacheDir(), { recursive: true });
|
|
27831
27008
|
const entry = {
|
|
27832
27009
|
schemaVersion: CACHE_SCHEMA_VERSION,
|
|
27833
27010
|
cachedAt: Date.now(),
|
|
@@ -27835,7 +27012,6 @@ function writeCachedTranscriptResult(transcriptPath, mtimeMs, sizeBytes, result,
|
|
|
27835
27012
|
sizeBytes,
|
|
27836
27013
|
engineVersion: getEngineVersion(),
|
|
27837
27014
|
detectorVersion: getDetectorVersion(),
|
|
27838
|
-
leakScanVersion: LEAK_SCAN_VERSION,
|
|
27839
27015
|
result
|
|
27840
27016
|
};
|
|
27841
27017
|
if (resume && resume.bytesScanned > 0) {
|
|
@@ -27846,9 +27022,9 @@ function writeCachedTranscriptResult(transcriptPath, mtimeMs, sizeBytes, result,
|
|
|
27846
27022
|
entry.detectorState = encodeDetectorState(resume.detectorState);
|
|
27847
27023
|
}
|
|
27848
27024
|
}
|
|
27849
|
-
|
|
27025
|
+
writeFileSync17(cachePath, JSON.stringify(entry), { encoding: "utf-8", mode: 384 });
|
|
27850
27026
|
try {
|
|
27851
|
-
|
|
27027
|
+
chmodSync5(cachePath, 384);
|
|
27852
27028
|
} catch {}
|
|
27853
27029
|
} catch {}
|
|
27854
27030
|
}
|
|
@@ -27856,7 +27032,6 @@ var cachedEngineVersion = null, cachedDetectorVersion = null, CACHE_SCHEMA_VERSI
|
|
|
27856
27032
|
var init_cache = __esm(() => {
|
|
27857
27033
|
init_builtin_policies();
|
|
27858
27034
|
init_detectors();
|
|
27859
|
-
init_leak_scan();
|
|
27860
27035
|
init_fp_home();
|
|
27861
27036
|
CACHE_TTL_MS = 30 * 24 * 60 * 60000;
|
|
27862
27037
|
});
|
|
@@ -27925,30 +27100,6 @@ var init_replay = __esm(() => {
|
|
|
27925
27100
|
});
|
|
27926
27101
|
|
|
27927
27102
|
// src/audit/index.ts
|
|
27928
|
-
class ByteGate {
|
|
27929
|
-
budget;
|
|
27930
|
-
inFlight = 0;
|
|
27931
|
-
waiting = [];
|
|
27932
|
-
constructor(budget) {
|
|
27933
|
-
this.budget = budget;
|
|
27934
|
-
}
|
|
27935
|
-
async acquire(bytes) {
|
|
27936
|
-
const want = Math.max(0, bytes);
|
|
27937
|
-
while (this.inFlight > 0 && this.inFlight + want > this.budget) {
|
|
27938
|
-
await new Promise((resolve) => this.waiting.push(resolve));
|
|
27939
|
-
}
|
|
27940
|
-
this.inFlight += want;
|
|
27941
|
-
}
|
|
27942
|
-
release(bytes) {
|
|
27943
|
-
this.inFlight -= Math.max(0, bytes);
|
|
27944
|
-
if (this.inFlight < 0)
|
|
27945
|
-
this.inFlight = 0;
|
|
27946
|
-
const waiters = this.waiting;
|
|
27947
|
-
this.waiting = [];
|
|
27948
|
-
for (const wake of waiters)
|
|
27949
|
-
wake();
|
|
27950
|
-
}
|
|
27951
|
-
}
|
|
27952
27103
|
function shortPolicyName(name) {
|
|
27953
27104
|
const slash = name.indexOf("/");
|
|
27954
27105
|
return slash >= 0 ? name.slice(slash + 1) : name;
|
|
@@ -27990,27 +27141,6 @@ function parseSinceOpt(since) {
|
|
|
27990
27141
|
return t;
|
|
27991
27142
|
throw new Error(`Invalid --since value: "${since}" (expected e.g. "7d", "30d", or "2026-04-01")`);
|
|
27992
27143
|
}
|
|
27993
|
-
function isCredentialResearchSession(events, sessionDescription) {
|
|
27994
|
-
if (sessionDescription && CREDENTIAL_RESEARCH_PURPOSE.test(sessionDescription))
|
|
27995
|
-
return true;
|
|
27996
|
-
const matched = new Set;
|
|
27997
|
-
let evidenceEvents = 0;
|
|
27998
|
-
for (const event of events) {
|
|
27999
|
-
const intent = flattenToolInput(event.toolInput);
|
|
28000
|
-
let eventMatched = false;
|
|
28001
|
-
for (let i = 0;i < CREDENTIAL_RESEARCH_MARKERS.length; i++) {
|
|
28002
|
-
if (CREDENTIAL_RESEARCH_MARKERS[i].test(intent)) {
|
|
28003
|
-
matched.add(i);
|
|
28004
|
-
eventMatched = true;
|
|
28005
|
-
}
|
|
28006
|
-
}
|
|
28007
|
-
if (eventMatched)
|
|
28008
|
-
evidenceEvents++;
|
|
28009
|
-
if (matched.size >= 2 || evidenceEvents >= 3)
|
|
28010
|
-
return true;
|
|
28011
|
-
}
|
|
28012
|
-
return false;
|
|
28013
|
-
}
|
|
28014
27144
|
async function scanOneTranscript(meta, resume) {
|
|
28015
27145
|
const empty = {
|
|
28016
27146
|
transcriptPath: meta.transcriptPath,
|
|
@@ -28051,10 +27181,13 @@ async function scanOneTranscript(meta, resume) {
|
|
|
28051
27181
|
const result = empty;
|
|
28052
27182
|
result.eventsScanned = events.length;
|
|
28053
27183
|
result.cwd = result.cwd || events[0].cwd || "";
|
|
28054
|
-
const suppressLeakScan = isCredentialResearchSession(events, meta.sessionDescription);
|
|
28055
|
-
if (suppressLeakScan)
|
|
28056
|
-
result.leakScanSuppressed = "credential-research";
|
|
28057
27184
|
for (const event of events) {
|
|
27185
|
+
for (const detector of AUDIT_DETECTORS) {
|
|
27186
|
+
const hit = detector.detect(event, sessionState);
|
|
27187
|
+
if (!hit)
|
|
27188
|
+
continue;
|
|
27189
|
+
recordHit(result, detector.name, event.timestamp, event.cwd, truncateExample(hit.example));
|
|
27190
|
+
}
|
|
28058
27191
|
let replayHits;
|
|
28059
27192
|
try {
|
|
28060
27193
|
replayHits = await replayEvent(event);
|
|
@@ -28065,77 +27198,9 @@ async function scanOneTranscript(meta, resume) {
|
|
|
28065
27198
|
const example = formatPolicyExample(hit.policyName, event);
|
|
28066
27199
|
recordHit(result, hit.policyName, event.timestamp, event.cwd, truncateExample(example));
|
|
28067
27200
|
}
|
|
28068
|
-
if (!suppressLeakScan)
|
|
28069
|
-
recordLeaks(result, event);
|
|
28070
27201
|
}
|
|
28071
27202
|
return { result, bytesScanned, detectorState: sessionState, resumed };
|
|
28072
27203
|
}
|
|
28073
|
-
function recordLeaks(result, event) {
|
|
28074
|
-
const salt = leakSalt();
|
|
28075
|
-
if (!salt)
|
|
28076
|
-
return;
|
|
28077
|
-
const filePath = event.toolInput.file_path;
|
|
28078
|
-
const shortPath = typeof filePath === "string" ? shortenPaths(filePath) : null;
|
|
28079
|
-
const scan = (text, direction) => {
|
|
28080
|
-
for (const match of findSecrets(text)) {
|
|
28081
|
-
(result.leaks ??= []).push({
|
|
28082
|
-
id: fingerprintId(match.value, salt),
|
|
28083
|
-
fingerprint: fingerprintSecret(match.value, match.rule),
|
|
28084
|
-
name: match.name,
|
|
28085
|
-
rule: match.rule,
|
|
28086
|
-
shaped: match.shaped,
|
|
28087
|
-
timestamp: event.timestamp,
|
|
28088
|
-
cwd: event.cwd,
|
|
28089
|
-
toolName: event.toolName,
|
|
28090
|
-
direction,
|
|
28091
|
-
path: shortPath
|
|
28092
|
-
});
|
|
28093
|
-
}
|
|
28094
|
-
};
|
|
28095
|
-
scan(flattenToolInput(event.toolInput), "input");
|
|
28096
|
-
if (event.toolResultText !== undefined)
|
|
28097
|
-
scan(event.toolResultText, "result");
|
|
28098
|
-
}
|
|
28099
|
-
function leakSalt() {
|
|
28100
|
-
if (cachedSalt === undefined) {
|
|
28101
|
-
try {
|
|
28102
|
-
cachedSalt = readLeakIdentity().salt;
|
|
28103
|
-
} catch {
|
|
28104
|
-
cachedSalt = null;
|
|
28105
|
-
}
|
|
28106
|
-
}
|
|
28107
|
-
return cachedSalt;
|
|
28108
|
-
}
|
|
28109
|
-
function persistLeaks(perTranscript, replaceExisting) {
|
|
28110
|
-
const fresh = [];
|
|
28111
|
-
try {
|
|
28112
|
-
const previous = readLeakRecord();
|
|
28113
|
-
const previousIds = new Set(previous.findings.map((f) => f.id));
|
|
28114
|
-
const record = replaceExisting ? emptyRecord(previous.salt, new Date().toISOString()) : previous;
|
|
28115
|
-
for (const t of perTranscript) {
|
|
28116
|
-
for (const leak of t.leaks ?? []) {
|
|
28117
|
-
const { isNew } = upsertFinding(record, {
|
|
28118
|
-
id: leak.id,
|
|
28119
|
-
fingerprint: leak.fingerprint,
|
|
28120
|
-
name: leak.name,
|
|
28121
|
-
rule: leak.rule,
|
|
28122
|
-
confidence: leak.shaped ? "doc-verified" : "name-only",
|
|
28123
|
-
sighting: {
|
|
28124
|
-
cli: t.cli,
|
|
28125
|
-
sessionId: t.sessionId,
|
|
28126
|
-
cwd: shortenPaths(leak.cwd),
|
|
28127
|
-
at: leak.timestamp,
|
|
28128
|
-
mechanism: describeMechanism(leak.toolName, leak.direction, leak.path)
|
|
28129
|
-
}
|
|
28130
|
-
});
|
|
28131
|
-
if (isNew && !previousIds.has(leak.id))
|
|
28132
|
-
fresh.push(leak.id);
|
|
28133
|
-
}
|
|
28134
|
-
}
|
|
28135
|
-
writeLeakRecord(record);
|
|
28136
|
-
} catch {}
|
|
28137
|
-
return fresh;
|
|
28138
|
-
}
|
|
28139
27204
|
function formatPolicyExample(_policyName, event) {
|
|
28140
27205
|
if (event.toolName === "Bash") {
|
|
28141
27206
|
const command = event.toolInput.command;
|
|
@@ -28158,12 +27223,6 @@ function mergeIncremental(cached, tail) {
|
|
|
28158
27223
|
examplesByName: {},
|
|
28159
27224
|
rangeByName: { ...cached.rangeByName }
|
|
28160
27225
|
};
|
|
28161
|
-
if (cached.leakScanSuppressed || tail.leakScanSuppressed) {
|
|
28162
|
-
out.leakScanSuppressed = "credential-research";
|
|
28163
|
-
out.leaks = [];
|
|
28164
|
-
} else {
|
|
28165
|
-
out.leaks = [...cached.leaks ?? [], ...tail.leaks ?? []];
|
|
28166
|
-
}
|
|
28167
27226
|
for (const [name, list] of Object.entries(cached.examplesByName)) {
|
|
28168
27227
|
out.examplesByName[name] = [...list];
|
|
28169
27228
|
}
|
|
@@ -28287,19 +27346,16 @@ async function runAuditInner(opts, startedAt) {
|
|
|
28287
27346
|
}
|
|
28288
27347
|
} catch {}
|
|
28289
27348
|
const allTranscripts = [];
|
|
28290
|
-
let discoveryErrors = 0;
|
|
28291
27349
|
for (const cli of clis) {
|
|
28292
27350
|
const adapter = ADAPTERS[cli];
|
|
28293
27351
|
let list;
|
|
28294
27352
|
try {
|
|
28295
27353
|
list = await adapter.listTranscripts({ projects: opts.projects, sinceMs });
|
|
28296
27354
|
} catch {
|
|
28297
|
-
discoveryErrors++;
|
|
28298
27355
|
continue;
|
|
28299
27356
|
}
|
|
28300
27357
|
allTranscripts.push(...list);
|
|
28301
27358
|
}
|
|
28302
|
-
const gate = new ByteGate(TRANSCRIPT_BYTE_BUDGET);
|
|
28303
27359
|
let skipped = 0;
|
|
28304
27360
|
let errors = 0;
|
|
28305
27361
|
const tasks = allTranscripts.map((meta) => async () => {
|
|
@@ -28312,7 +27368,6 @@ async function runAuditInner(opts, startedAt) {
|
|
|
28312
27368
|
resume = found?.kind === "resume" ? { fromByte: found.fromByte, detectorState: found.detectorState } : undefined;
|
|
28313
27369
|
cachedPrefix = found?.kind === "resume" ? found.result : null;
|
|
28314
27370
|
}
|
|
28315
|
-
await gate.acquire(meta.sizeBytes);
|
|
28316
27371
|
try {
|
|
28317
27372
|
const scan = await scanOneTranscript(meta, resume);
|
|
28318
27373
|
const fresh = cachedPrefix && scan.resumed ? mergeIncremental(cachedPrefix, scan.result) : scan.result;
|
|
@@ -28335,8 +27390,6 @@ async function runAuditInner(opts, startedAt) {
|
|
|
28335
27390
|
examplesByName: {},
|
|
28336
27391
|
rangeByName: {}
|
|
28337
27392
|
};
|
|
28338
|
-
} finally {
|
|
28339
|
-
gate.release(meta.sizeBytes);
|
|
28340
27393
|
}
|
|
28341
27394
|
});
|
|
28342
27395
|
const settled = await batchAll(tasks, TRANSCRIPT_CONCURRENCY);
|
|
@@ -28363,13 +27416,8 @@ async function runAuditInner(opts, startedAt) {
|
|
|
28363
27416
|
projectsScannedSet.add(t.cwd);
|
|
28364
27417
|
eventsScanned += t.eventsScanned ?? 0;
|
|
28365
27418
|
}
|
|
28366
|
-
const coversAllHistory = opts.clis === undefined && opts.projects === undefined && opts.since === undefined && discoveryErrors === 0 && errors === 0 && skipped === 0;
|
|
28367
|
-
const newFindingIds = persistLeaks(perTranscript, coversAllHistory);
|
|
28368
|
-
const leakIds = [...new Set(perTranscript.flatMap((t) => (t.leaks ?? []).map((leak) => leak.id)))];
|
|
28369
27419
|
const auditResult = {
|
|
28370
27420
|
version: 2,
|
|
28371
|
-
leakIds,
|
|
28372
|
-
newLeakIds: newFindingIds,
|
|
28373
27421
|
scannedAt: new Date(startedAt).toISOString(),
|
|
28374
27422
|
scope: {
|
|
28375
27423
|
cli: clis,
|
|
@@ -28393,7 +27441,7 @@ async function runAuditInner(opts, startedAt) {
|
|
|
28393
27441
|
};
|
|
28394
27442
|
return auditResult;
|
|
28395
27443
|
}
|
|
28396
|
-
var TRANSCRIPT_CONCURRENCY = 8
|
|
27444
|
+
var TRANSCRIPT_CONCURRENCY = 8;
|
|
28397
27445
|
var init_audit = __esm(() => {
|
|
28398
27446
|
init_builtin_policies();
|
|
28399
27447
|
init_hooks_config();
|
|
@@ -28402,33 +27450,20 @@ var init_audit = __esm(() => {
|
|
|
28402
27450
|
init_cli_adapters();
|
|
28403
27451
|
init_detectors();
|
|
28404
27452
|
init_features();
|
|
28405
|
-
init_leak_scan();
|
|
28406
|
-
init_leak_fingerprint();
|
|
28407
|
-
init_leak_store();
|
|
28408
|
-
init_redact_example();
|
|
28409
27453
|
init_cache();
|
|
28410
27454
|
init_replay();
|
|
28411
27455
|
init_types2();
|
|
28412
|
-
TRANSCRIPT_BYTE_BUDGET = 48 * 1024 * 1024;
|
|
28413
|
-
CREDENTIAL_RESEARCH_MARKERS = [
|
|
28414
|
-
/\b(?:secret|credential)[-_ ](?:scanner|detection|detector|pattern|regex|corpus)\b/i,
|
|
28415
|
-
/\b(?:trufflehog|trufflesecurity|gitleaks|detect-secrets|secret-detection-rules)\b/i,
|
|
28416
|
-
/\b(?:SECRET_PATTERNS|findSecrets|isDocsLiteral|leak-scan)\b/,
|
|
28417
|
-
/\b(?:fixture|placeholder|synthetic|sample) (?:api )?(?:key|token|secret|credential)s?\b/i,
|
|
28418
|
-
/\b(?:grep|rg|ripgrep|scan|hunt|search)\b[^\n]{0,240}\b(?:secret|credential|token)s?\b/i
|
|
28419
|
-
];
|
|
28420
|
-
CREDENTIAL_RESEARCH_PURPOSE = /\b(?:hunt|scan|search|research|test)(?:ing)?\b[^\n]{0,80}\b(?:secret|credential|token)s?\b|\b(?:secret|credential|token)s?\b[^\n]{0,80}\b(?:scanner|detector|detection|research|fixtures?|corpus)\b/i;
|
|
28421
27456
|
});
|
|
28422
27457
|
|
|
28423
27458
|
// src/audit/audit-lock.ts
|
|
28424
27459
|
import {
|
|
28425
27460
|
closeSync as closeSync2,
|
|
28426
27461
|
linkSync,
|
|
28427
|
-
mkdirSync as
|
|
27462
|
+
mkdirSync as mkdirSync20,
|
|
28428
27463
|
openSync as openSync2,
|
|
28429
|
-
readFileSync as
|
|
27464
|
+
readFileSync as readFileSync26,
|
|
28430
27465
|
unlinkSync as unlinkSync4,
|
|
28431
|
-
writeFileSync as
|
|
27466
|
+
writeFileSync as writeFileSync18,
|
|
28432
27467
|
writeSync
|
|
28433
27468
|
} from "node:fs";
|
|
28434
27469
|
function acquireAuditLock(source, now = Date.now()) {
|
|
@@ -28487,7 +27522,7 @@ function handleFor(path, info) {
|
|
|
28487
27522
|
}
|
|
28488
27523
|
function readLock2(path) {
|
|
28489
27524
|
try {
|
|
28490
|
-
const parsed = JSON.parse(
|
|
27525
|
+
const parsed = JSON.parse(readFileSync26(path, "utf8"));
|
|
28491
27526
|
if (typeof parsed?.pid !== "number" || typeof parsed?.startedAt !== "number")
|
|
28492
27527
|
return null;
|
|
28493
27528
|
return {
|
|
@@ -28503,7 +27538,7 @@ function writeLockExclusive(path, info) {
|
|
|
28503
27538
|
const body = JSON.stringify(info);
|
|
28504
27539
|
const staging = `${path}.${process.pid}.tmp`;
|
|
28505
27540
|
try {
|
|
28506
|
-
|
|
27541
|
+
writeFileSync18(staging, body, { mode: 384 });
|
|
28507
27542
|
} catch {
|
|
28508
27543
|
return false;
|
|
28509
27544
|
}
|
|
@@ -28537,8 +27572,8 @@ function createThenWrite(path, body) {
|
|
|
28537
27572
|
}
|
|
28538
27573
|
}
|
|
28539
27574
|
function ensureRunDir() {
|
|
28540
|
-
|
|
28541
|
-
|
|
27575
|
+
mkdirSync20(failproofaiHome(), { recursive: true });
|
|
27576
|
+
mkdirSync20(runDir(), { recursive: true, mode: 448 });
|
|
28542
27577
|
}
|
|
28543
27578
|
var AUDIT_LOCK_MAX_AGE_MS;
|
|
28544
27579
|
var init_audit_lock = __esm(() => {
|
|
@@ -28547,7 +27582,7 @@ var init_audit_lock = __esm(() => {
|
|
|
28547
27582
|
});
|
|
28548
27583
|
|
|
28549
27584
|
// src/audit/dashboard-cache.ts
|
|
28550
|
-
import { existsSync as
|
|
27585
|
+
import { existsSync as existsSync31, readFileSync as readFileSync27 } from "node:fs";
|
|
28551
27586
|
function getCachePath() {
|
|
28552
27587
|
return auditDashboardFile();
|
|
28553
27588
|
}
|
|
@@ -28567,10 +27602,10 @@ function writeDashboardCache(params, result) {
|
|
|
28567
27602
|
}
|
|
28568
27603
|
function readDashboardCacheMeta() {
|
|
28569
27604
|
const cachePath = getCachePath();
|
|
28570
|
-
if (!
|
|
27605
|
+
if (!existsSync31(cachePath))
|
|
28571
27606
|
return null;
|
|
28572
27607
|
try {
|
|
28573
|
-
const raw =
|
|
27608
|
+
const raw = readFileSync27(cachePath, "utf-8");
|
|
28574
27609
|
const entry = JSON.parse(raw);
|
|
28575
27610
|
if (!entry || entry.schemaVersion !== DASHBOARD_CACHE_SCHEMA_VERSION || typeof entry.cachedAt !== "string" || Number.isNaN(new Date(entry.cachedAt).getTime()))
|
|
28576
27611
|
return null;
|
|
@@ -28794,18 +27829,18 @@ var init_api_server_client = __esm(() => {
|
|
|
28794
27829
|
});
|
|
28795
27830
|
|
|
28796
27831
|
// lib/auth/auth-store.ts
|
|
28797
|
-
import { existsSync as
|
|
28798
|
-
import { join as join34, resolve as
|
|
27832
|
+
import { existsSync as existsSync32, readFileSync as readFileSync28, readdirSync as readdirSync17, rmSync as rmSync12 } from "node:fs";
|
|
27833
|
+
import { join as join34, resolve as resolve23 } from "node:path";
|
|
28799
27834
|
function getAuthFilePath() {
|
|
28800
27835
|
const override = process.env.FAILPROOFAI_AUTH_DIR;
|
|
28801
27836
|
return override ? join34(override, "session.json") : auditSessionFile();
|
|
28802
27837
|
}
|
|
28803
27838
|
function readAuth() {
|
|
28804
27839
|
const p = getAuthFilePath();
|
|
28805
|
-
if (!
|
|
27840
|
+
if (!existsSync32(p))
|
|
28806
27841
|
return null;
|
|
28807
27842
|
try {
|
|
28808
|
-
const raw =
|
|
27843
|
+
const raw = readFileSync28(p, "utf-8");
|
|
28809
27844
|
const parsed = JSON.parse(raw);
|
|
28810
27845
|
if (typeof parsed.access_token !== "string" || typeof parsed.refresh_token !== "string" || typeof parsed.access_expires_at !== "number" || typeof parsed.user !== "object" || !parsed.user || typeof parsed.user.id !== "string" || typeof parsed.user.email !== "string") {
|
|
28811
27846
|
return null;
|
|
@@ -28829,18 +27864,18 @@ function writeAuth(auth) {
|
|
|
28829
27864
|
}
|
|
28830
27865
|
function deleteAuth() {
|
|
28831
27866
|
const p = getAuthFilePath();
|
|
28832
|
-
if (
|
|
28833
|
-
|
|
27867
|
+
if (existsSync32(p))
|
|
27868
|
+
rmSync12(p, { force: true });
|
|
28834
27869
|
try {
|
|
28835
27870
|
const root = migrationsDir();
|
|
28836
|
-
if (!
|
|
27871
|
+
if (!existsSync32(root))
|
|
28837
27872
|
return;
|
|
28838
|
-
for (const entry of
|
|
27873
|
+
for (const entry of readdirSync17(root)) {
|
|
28839
27874
|
if (!entry.startsWith("backup-layout"))
|
|
28840
27875
|
continue;
|
|
28841
|
-
const copy =
|
|
28842
|
-
if (
|
|
28843
|
-
|
|
27876
|
+
const copy = resolve23(root, entry, "auth.json");
|
|
27877
|
+
if (existsSync32(copy))
|
|
27878
|
+
rmSync12(copy, { force: true });
|
|
28844
27879
|
}
|
|
28845
27880
|
} catch {}
|
|
28846
27881
|
}
|
|
@@ -28900,6 +27935,112 @@ var init_auth_store = __esm(() => {
|
|
|
28900
27935
|
inFlightRefreshes = new Map;
|
|
28901
27936
|
});
|
|
28902
27937
|
|
|
27938
|
+
// src/audit/redact-example.ts
|
|
27939
|
+
import { homedir as homedir26 } from "node:os";
|
|
27940
|
+
function maskTruncatedSecret(input) {
|
|
27941
|
+
for (const [pattern, label] of SECRET_PREFIXES) {
|
|
27942
|
+
if (pattern.test(input)) {
|
|
27943
|
+
return input.replace(pattern, `[REDACTED: ${label}]`);
|
|
27944
|
+
}
|
|
27945
|
+
}
|
|
27946
|
+
return input;
|
|
27947
|
+
}
|
|
27948
|
+
function isSecretName(name) {
|
|
27949
|
+
const upper = name.toUpperCase();
|
|
27950
|
+
if (SECRET_NAME_SUBSTRINGS.some((word) => upper.includes(word)))
|
|
27951
|
+
return true;
|
|
27952
|
+
return upper.split("_").some((part) => SECRET_NAME_COMPONENTS.includes(part));
|
|
27953
|
+
}
|
|
27954
|
+
function maskAssignedSecrets(input) {
|
|
27955
|
+
let out = input.replace(ASSIGNMENT_RE, (match, name, value) => {
|
|
27956
|
+
if (!isSecretName(name))
|
|
27957
|
+
return match;
|
|
27958
|
+
if (value.startsWith("[REDACTED"))
|
|
27959
|
+
return match;
|
|
27960
|
+
return `${name}=[REDACTED: assigned secret]`;
|
|
27961
|
+
});
|
|
27962
|
+
out = out.replace(URL_CREDENTIALS_RE, "$1[REDACTED: URL credentials]@");
|
|
27963
|
+
out = out.replace(BASIC_AUTH_FLAG_RE, "$1[REDACTED: basic auth]");
|
|
27964
|
+
return out;
|
|
27965
|
+
}
|
|
27966
|
+
function maskSecrets(input) {
|
|
27967
|
+
let out = input;
|
|
27968
|
+
for (const [pattern, label] of SECRET_PATTERNS) {
|
|
27969
|
+
const global = new RegExp(pattern.source, pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`);
|
|
27970
|
+
out = out.replace(global, `[REDACTED: ${label}]`);
|
|
27971
|
+
}
|
|
27972
|
+
return out;
|
|
27973
|
+
}
|
|
27974
|
+
function shortenPaths(input, home = homedir26()) {
|
|
27975
|
+
const homeRoot = home.replace(/\/+$/, "");
|
|
27976
|
+
return input.replace(ABSOLUTE_PATH_RE, (match, offset, whole) => {
|
|
27977
|
+
if (PUBLIC_PATH_ROOTS.some((root) => match.startsWith(root)))
|
|
27978
|
+
return match;
|
|
27979
|
+
if (offset > 0 && whole[offset - 1] === "/" && /[a-z][a-z0-9+.\-]*:$/i.test(whole.slice(0, offset - 1))) {
|
|
27980
|
+
const urlSegments = match.split("/").filter(Boolean);
|
|
27981
|
+
if (urlSegments.length <= 1)
|
|
27982
|
+
return match;
|
|
27983
|
+
const host = urlSegments[0];
|
|
27984
|
+
const leaf = urlSegments[urlSegments.length - 1];
|
|
27985
|
+
const elided = urlSegments.length > 2 ? "/…" : "";
|
|
27986
|
+
return `/${host}${elided}/${leaf}${match.endsWith("/") ? "/" : ""}`;
|
|
27987
|
+
}
|
|
27988
|
+
const trailingSlash = match.endsWith("/");
|
|
27989
|
+
const segments = match.split("/").filter(Boolean);
|
|
27990
|
+
if (segments.length === 0)
|
|
27991
|
+
return match;
|
|
27992
|
+
const basename = segments[segments.length - 1];
|
|
27993
|
+
const kept = segments.slice(Math.max(0, segments.length - 1 - KEPT_PARENT_SEGMENTS), segments.length - 1);
|
|
27994
|
+
const matchRoot = match.replace(/\/+$/, "");
|
|
27995
|
+
const underHome = homeRoot.length > 0 && (matchRoot === homeRoot || matchRoot.startsWith(`${homeRoot}/`));
|
|
27996
|
+
if (matchRoot === homeRoot && homeRoot.length > 0) {
|
|
27997
|
+
return trailingSlash ? "~/" : "~";
|
|
27998
|
+
}
|
|
27999
|
+
const root = underHome ? "~" : "";
|
|
28000
|
+
const middle = segments.length - kept.length - 1 > 0 ? "/…" : "";
|
|
28001
|
+
const tail = [...kept, basename].join("/");
|
|
28002
|
+
return `${root}${middle}/${tail}${trailingSlash ? "/" : ""}`;
|
|
28003
|
+
});
|
|
28004
|
+
}
|
|
28005
|
+
function redactExample(input, home = homedir26()) {
|
|
28006
|
+
const masked = maskAssignedSecrets(maskTruncatedSecret(maskSecrets(input)));
|
|
28007
|
+
const shortened = shortenPaths(masked, home);
|
|
28008
|
+
const collapsed = shortened.replace(/\s+/g, " ").trim();
|
|
28009
|
+
return collapsed.length > REDACTED_EXAMPLE_MAX_CHARS ? `${collapsed.slice(0, REDACTED_EXAMPLE_MAX_CHARS - 1)}…` : collapsed;
|
|
28010
|
+
}
|
|
28011
|
+
var REDACTED_EXAMPLE_MAX_CHARS = 160, KEPT_PARENT_SEGMENTS = 0, ABSOLUTE_PATH_RE, PUBLIC_PATH_ROOTS, SECRET_PREFIXES, SECRET_NAME_SUBSTRINGS, SECRET_NAME_COMPONENTS, ASSIGNMENT_RE, URL_CREDENTIALS_RE, BASIC_AUTH_FLAG_RE;
|
|
28012
|
+
var init_redact_example = __esm(() => {
|
|
28013
|
+
init_builtin_policies();
|
|
28014
|
+
ABSOLUTE_PATH_RE = /(?:\/[\w.\-@+]+){2,}\/?/g;
|
|
28015
|
+
PUBLIC_PATH_ROOTS = ["/dev/", "/proc/", "/sys/"];
|
|
28016
|
+
SECRET_PREFIXES = [
|
|
28017
|
+
[/(?<!\w)(?:Authorization:\s*)?Bearer\s+\S*$/i, "bearer token"],
|
|
28018
|
+
[/(?<!\w)sk-ant-\S*$/, "Anthropic API key"],
|
|
28019
|
+
[/(?<!\w)sk-proj-\S*$/, "OpenAI project API key"],
|
|
28020
|
+
[/(?<!\w)sk-\S*$/, "OpenAI API key"],
|
|
28021
|
+
[/(?<!\w)ghp_\S*$/, "GitHub personal access token"],
|
|
28022
|
+
[/(?<!\w)github_pat_\S*$/, "GitHub fine-grained token"],
|
|
28023
|
+
[/(?<!\w)AKIA\S*$/, "AWS access key ID"],
|
|
28024
|
+
[/(?<!\w)sk_live_\S*$/, "Stripe live secret key"],
|
|
28025
|
+
[/(?<!\w)sk_test_\S*$/, "Stripe test secret key"],
|
|
28026
|
+
[/(?<!\w)AIza\S*$/, "Google API key"],
|
|
28027
|
+
[/-----BEGIN\s[A-Z ]*$/, "private key"]
|
|
28028
|
+
];
|
|
28029
|
+
SECRET_NAME_SUBSTRINGS = [
|
|
28030
|
+
"TOKEN",
|
|
28031
|
+
"SECRET",
|
|
28032
|
+
"PASSWORD",
|
|
28033
|
+
"PASSWD",
|
|
28034
|
+
"CREDENTIAL",
|
|
28035
|
+
"APIKEY",
|
|
28036
|
+
"PRIVATEKEY"
|
|
28037
|
+
];
|
|
28038
|
+
SECRET_NAME_COMPONENTS = ["KEY", "PASS", "AUTH", "PAT", "SIG", "SIGNATURE", "SESSION", "COOKIE"];
|
|
28039
|
+
ASSIGNMENT_RE = /\b([A-Za-z_][A-Za-z0-9_]*)=("[^"]*"|'[^']*'|[^\s;|&"']+)/g;
|
|
28040
|
+
URL_CREDENTIALS_RE = /([a-z][a-z0-9+.\-]*:\/\/)[^\s/:@]+:[^\s/@]+@/gi;
|
|
28041
|
+
BASIC_AUTH_FLAG_RE = /((?:^|\s)(?:-u|--user)[\s=])\S+:\S+/g;
|
|
28042
|
+
});
|
|
28043
|
+
|
|
28903
28044
|
// src/audit/harm-report.ts
|
|
28904
28045
|
function shortName2(name) {
|
|
28905
28046
|
const slash = name.indexOf("/");
|
|
@@ -28962,41 +28103,7 @@ function selectHarmful(result, from, to, opts = {}) {
|
|
|
28962
28103
|
out.sort((a, b) => b.hits - a.hits);
|
|
28963
28104
|
return out;
|
|
28964
28105
|
}
|
|
28965
|
-
function
|
|
28966
|
-
const lo = from.getTime();
|
|
28967
|
-
const hi = to.getTime();
|
|
28968
|
-
const out = [];
|
|
28969
|
-
for (const f of findings) {
|
|
28970
|
-
if (f.dismissedAt)
|
|
28971
|
-
continue;
|
|
28972
|
-
const last = ts(f.lastSeen);
|
|
28973
|
-
if (last !== null && (last < lo || last > hi))
|
|
28974
|
-
continue;
|
|
28975
|
-
const seen = f.sightings?.reduce((latest, candidate) => {
|
|
28976
|
-
if (!latest)
|
|
28977
|
-
return candidate;
|
|
28978
|
-
return (ts(candidate.at) ?? 0) >= (ts(latest.at) ?? 0) ? candidate : latest;
|
|
28979
|
-
}, undefined);
|
|
28980
|
-
out.push({
|
|
28981
|
-
id: f.id,
|
|
28982
|
-
display: f.fingerprint?.display ?? "[credential]",
|
|
28983
|
-
label: f.fingerprint?.label ?? "secret",
|
|
28984
|
-
length: f.fingerprint?.length ?? 0,
|
|
28985
|
-
attributed: f.fingerprint?.attributed === true,
|
|
28986
|
-
name: f.name,
|
|
28987
|
-
confidence: f.confidence,
|
|
28988
|
-
first_seen: f.firstSeen,
|
|
28989
|
-
last_seen: f.lastSeen,
|
|
28990
|
-
occurrences: f.occurrences,
|
|
28991
|
-
cli: seen?.cli ?? "unknown",
|
|
28992
|
-
project: seen?.cwd ?? "unknown",
|
|
28993
|
-
mechanism: seen?.mechanism?.summary ?? "seen in a transcript",
|
|
28994
|
-
direction: seen?.mechanism?.direction ?? "result"
|
|
28995
|
-
});
|
|
28996
|
-
}
|
|
28997
|
-
return out.sort((a, b) => (ts(b.last_seen) ?? 0) - (ts(a.last_seen) ?? 0));
|
|
28998
|
-
}
|
|
28999
|
-
function buildHarmReport(result, lastReportedAt, intervalDays, leakFindings = []) {
|
|
28106
|
+
function buildHarmReport(result, lastReportedAt, intervalDays) {
|
|
29000
28107
|
const to = new Date(Date.parse(result.scannedAt));
|
|
29001
28108
|
const windowTo = Number.isFinite(to.getTime()) ? to : new Date;
|
|
29002
28109
|
const watermark = ts(lastReportedAt);
|
|
@@ -29007,8 +28114,7 @@ function buildHarmReport(result, lastReportedAt, intervalDays, leakFindings = []
|
|
|
29007
28114
|
return {
|
|
29008
28115
|
window_from: from.toISOString(),
|
|
29009
28116
|
window_to: windowTo.toISOString(),
|
|
29010
|
-
harmful: selectHarmful(result, from, windowTo, { includeUnplaceable: isFirstReport })
|
|
29011
|
-
leaks: selectLeaks(leakFindings, from, windowTo).slice(0, 1)
|
|
28117
|
+
harmful: selectHarmful(result, from, windowTo, { includeUnplaceable: isFirstReport })
|
|
29012
28118
|
};
|
|
29013
28119
|
}
|
|
29014
28120
|
var HARMFUL_SEVERITIES, ALSO_HARMFUL;
|
|
@@ -29019,15 +28125,15 @@ var init_harm_report = __esm(() => {
|
|
|
29019
28125
|
});
|
|
29020
28126
|
|
|
29021
28127
|
// src/audit/machine-store.ts
|
|
29022
|
-
import { existsSync as
|
|
28128
|
+
import { existsSync as existsSync33, readFileSync as readFileSync29, rmSync as rmSync13 } from "node:fs";
|
|
29023
28129
|
import { hostname as hostname5 } from "node:os";
|
|
29024
28130
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
29025
28131
|
function readMachineIdentity(home) {
|
|
29026
28132
|
const path = auditMachineFile(home);
|
|
29027
|
-
if (!
|
|
28133
|
+
if (!existsSync33(path))
|
|
29028
28134
|
return null;
|
|
29029
28135
|
try {
|
|
29030
|
-
const parsed = JSON.parse(
|
|
28136
|
+
const parsed = JSON.parse(readFileSync29(path, "utf8"));
|
|
29031
28137
|
if (typeof parsed.machine_id !== "string" || !parsed.machine_id)
|
|
29032
28138
|
return null;
|
|
29033
28139
|
return {
|
|
@@ -29073,20 +28179,20 @@ var init_machine_store = __esm(() => {
|
|
|
29073
28179
|
// src/audit/report-harm.ts
|
|
29074
28180
|
async function reportHarm(result) {
|
|
29075
28181
|
let auto = false;
|
|
28182
|
+
let consentedAt;
|
|
29076
28183
|
let intervalDays = 7;
|
|
29077
28184
|
try {
|
|
29078
28185
|
const config = readConfig();
|
|
29079
28186
|
auto = config.audit.auto;
|
|
28187
|
+
consentedAt = config.audit.reportsConsentedAt;
|
|
29080
28188
|
intervalDays = config.audit.intervalDays;
|
|
29081
28189
|
} catch {
|
|
29082
28190
|
return { kind: "disabled" };
|
|
29083
28191
|
}
|
|
29084
28192
|
if (!auto)
|
|
29085
28193
|
return { kind: "disabled" };
|
|
29086
|
-
|
|
29087
|
-
|
|
29088
|
-
if (report.leaks.length === 0)
|
|
29089
|
-
return { kind: "no-leak" };
|
|
28194
|
+
if (consentedAt === undefined)
|
|
28195
|
+
return { kind: "consent-required" };
|
|
29090
28196
|
const auth = await getValidAccessToken();
|
|
29091
28197
|
if (!auth) {
|
|
29092
28198
|
return { kind: "signed-out" };
|
|
@@ -29097,6 +28203,8 @@ async function reportHarm(result) {
|
|
|
29097
28203
|
} catch (err) {
|
|
29098
28204
|
return { kind: "failed", error: err instanceof Error ? err.message : String(err) };
|
|
29099
28205
|
}
|
|
28206
|
+
const report = buildHarmReport(result, identity.last_reported_at, intervalDays);
|
|
28207
|
+
const hits = report.harmful.reduce((n, p) => n + p.hits, 0);
|
|
29100
28208
|
try {
|
|
29101
28209
|
const res = await submitAuditReport(auth.access_token, {
|
|
29102
28210
|
machine_id: identity.machine_id,
|
|
@@ -29104,13 +28212,12 @@ async function reportHarm(result) {
|
|
|
29104
28212
|
platform: process.platform,
|
|
29105
28213
|
window_from: report.window_from,
|
|
29106
28214
|
window_to: report.window_to,
|
|
29107
|
-
harmful: report.harmful
|
|
29108
|
-
leaks: report.leaks
|
|
28215
|
+
harmful: report.harmful
|
|
29109
28216
|
});
|
|
29110
28217
|
try {
|
|
29111
28218
|
recordReportWatermark(res.next_window_from);
|
|
29112
28219
|
} catch {}
|
|
29113
|
-
return res.emailed ? { kind: "sent",
|
|
28220
|
+
return res.emailed ? { kind: "sent", hits } : { kind: "held", hits, reason: res.reason ?? "not_sent" };
|
|
29114
28221
|
} catch (err) {
|
|
29115
28222
|
const error = err instanceof AuthApiError ? `${err.code}: ${err.message}` : err instanceof Error ? err.message : String(err);
|
|
29116
28223
|
return { kind: "failed", error };
|
|
@@ -29119,16 +28226,17 @@ async function reportHarm(result) {
|
|
|
29119
28226
|
function describeOutcome2(outcome) {
|
|
29120
28227
|
switch (outcome.kind) {
|
|
29121
28228
|
case "disabled":
|
|
29122
|
-
case "no-leak":
|
|
29123
28229
|
return null;
|
|
28230
|
+
case "consent-required":
|
|
28231
|
+
return "failproofai: scheduled scans are on, but emailing what they find needs a fresh opt-in — run `failproofai audit --schedule` (or visit /settings) to turn digests on";
|
|
29124
28232
|
case "signed-out":
|
|
29125
|
-
return "failproofai:
|
|
28233
|
+
return "failproofai: emailed reports are on but this machine is signed out — run `failproofai audit --schedule` or visit /settings to resume them";
|
|
29126
28234
|
case "sent":
|
|
29127
|
-
return
|
|
28235
|
+
return `failproofai: emailed a harm digest (${outcome.hits} finding${outcome.hits === 1 ? "" : "s"})`;
|
|
29128
28236
|
case "held":
|
|
29129
|
-
return `failproofai:
|
|
28237
|
+
return `failproofai: ${outcome.hits} finding${outcome.hits === 1 ? "" : "s"} reported, no email (${outcome.reason})`;
|
|
29130
28238
|
case "failed":
|
|
29131
|
-
return `failproofai: could not send the
|
|
28239
|
+
return `failproofai: could not send the harm report: ${outcome.error}`;
|
|
29132
28240
|
}
|
|
29133
28241
|
}
|
|
29134
28242
|
var init_report_harm = __esm(() => {
|
|
@@ -29136,281 +28244,9 @@ var init_report_harm = __esm(() => {
|
|
|
29136
28244
|
init_api_server_client();
|
|
29137
28245
|
init_fp_config();
|
|
29138
28246
|
init_harm_report();
|
|
29139
|
-
init_leak_store();
|
|
29140
28247
|
init_machine_store();
|
|
29141
28248
|
});
|
|
29142
28249
|
|
|
29143
|
-
// src/audit/desktop-notify.ts
|
|
29144
|
-
import { connect } from "node:net";
|
|
29145
|
-
|
|
29146
|
-
class Marshal {
|
|
29147
|
-
chunks = [];
|
|
29148
|
-
len = 0;
|
|
29149
|
-
align(n) {
|
|
29150
|
-
const pad = (n - this.len % n) % n;
|
|
29151
|
-
if (pad)
|
|
29152
|
-
this.raw(Buffer.alloc(pad));
|
|
29153
|
-
}
|
|
29154
|
-
raw(b) {
|
|
29155
|
-
this.chunks.push(b);
|
|
29156
|
-
this.len += b.length;
|
|
29157
|
-
}
|
|
29158
|
-
byte(v) {
|
|
29159
|
-
this.raw(Buffer.from([v]));
|
|
29160
|
-
}
|
|
29161
|
-
u32(v) {
|
|
29162
|
-
this.align(4);
|
|
29163
|
-
const b = Buffer.alloc(4);
|
|
29164
|
-
b.writeUInt32LE(v, 0);
|
|
29165
|
-
this.raw(b);
|
|
29166
|
-
}
|
|
29167
|
-
i32(v) {
|
|
29168
|
-
this.align(4);
|
|
29169
|
-
const b = Buffer.alloc(4);
|
|
29170
|
-
b.writeInt32LE(v, 0);
|
|
29171
|
-
this.raw(b);
|
|
29172
|
-
}
|
|
29173
|
-
str(v) {
|
|
29174
|
-
const b = Buffer.from(v, "utf8");
|
|
29175
|
-
this.u32(b.length);
|
|
29176
|
-
this.raw(b);
|
|
29177
|
-
this.byte(0);
|
|
29178
|
-
}
|
|
29179
|
-
sig(v) {
|
|
29180
|
-
const b = Buffer.from(v, "utf8");
|
|
29181
|
-
this.byte(b.length);
|
|
29182
|
-
this.raw(b);
|
|
29183
|
-
this.byte(0);
|
|
29184
|
-
}
|
|
29185
|
-
get length() {
|
|
29186
|
-
return this.len;
|
|
29187
|
-
}
|
|
29188
|
-
buffer() {
|
|
29189
|
-
return Buffer.concat(this.chunks);
|
|
29190
|
-
}
|
|
29191
|
-
}
|
|
29192
|
-
function headerFields(fields) {
|
|
29193
|
-
const m = new Marshal;
|
|
29194
|
-
for (const [code, signature, value] of fields) {
|
|
29195
|
-
m.align(8);
|
|
29196
|
-
m.byte(code);
|
|
29197
|
-
m.sig(signature);
|
|
29198
|
-
if (signature === "g")
|
|
29199
|
-
m.sig(value);
|
|
29200
|
-
else
|
|
29201
|
-
m.str(value);
|
|
29202
|
-
}
|
|
29203
|
-
return { data: m.buffer(), declaredLength: m.length };
|
|
29204
|
-
}
|
|
29205
|
-
function methodCall(serial, fields, body) {
|
|
29206
|
-
const { data, declaredLength } = headerFields(fields);
|
|
29207
|
-
const head = new Marshal;
|
|
29208
|
-
head.raw(Buffer.from([108, 1, 0, 1]));
|
|
29209
|
-
head.u32(body.length);
|
|
29210
|
-
head.u32(serial);
|
|
29211
|
-
head.u32(declaredLength);
|
|
29212
|
-
head.raw(data);
|
|
29213
|
-
head.align(8);
|
|
29214
|
-
return Buffer.concat([head.buffer(), body]);
|
|
29215
|
-
}
|
|
29216
|
-
function notifyBody(app, replacesId, summary, body) {
|
|
29217
|
-
const m = new Marshal;
|
|
29218
|
-
m.str(app);
|
|
29219
|
-
m.u32(replacesId);
|
|
29220
|
-
m.str("");
|
|
29221
|
-
m.str(summary);
|
|
29222
|
-
m.str(body);
|
|
29223
|
-
m.u32(0);
|
|
29224
|
-
m.u32(0);
|
|
29225
|
-
m.align(8);
|
|
29226
|
-
m.i32(-1);
|
|
29227
|
-
return m.buffer();
|
|
29228
|
-
}
|
|
29229
|
-
function readMessage(buf) {
|
|
29230
|
-
if (buf.length < 16)
|
|
29231
|
-
return null;
|
|
29232
|
-
const bodyLength = buf.readUInt32LE(4);
|
|
29233
|
-
const fieldsLength = buf.readUInt32LE(12);
|
|
29234
|
-
const bodyStart = 16 + fieldsLength + (8 - fieldsLength % 8) % 8;
|
|
29235
|
-
const total = bodyStart + bodyLength;
|
|
29236
|
-
if (buf.length < total)
|
|
29237
|
-
return null;
|
|
29238
|
-
let replySerial = null;
|
|
29239
|
-
let errorName = null;
|
|
29240
|
-
let i = 16;
|
|
29241
|
-
const end = 16 + fieldsLength;
|
|
29242
|
-
while (i < end) {
|
|
29243
|
-
i += (8 - (i - 16) % 8) % 8;
|
|
29244
|
-
if (i + 4 > end)
|
|
29245
|
-
break;
|
|
29246
|
-
const code = buf[i];
|
|
29247
|
-
const sigLen = buf[i + 1];
|
|
29248
|
-
const sig = buf.toString("latin1", i + 2, i + 2 + sigLen);
|
|
29249
|
-
i += 2 + sigLen + 1;
|
|
29250
|
-
if (sig === "u") {
|
|
29251
|
-
i += (4 - (i - 16) % 4) % 4;
|
|
29252
|
-
if (i + 4 > end)
|
|
29253
|
-
break;
|
|
29254
|
-
if (code === 5)
|
|
29255
|
-
replySerial = buf.readUInt32LE(i);
|
|
29256
|
-
i += 4;
|
|
29257
|
-
} else if (sig === "s" || sig === "o") {
|
|
29258
|
-
i += (4 - (i - 16) % 4) % 4;
|
|
29259
|
-
if (i + 4 > end)
|
|
29260
|
-
break;
|
|
29261
|
-
const len = buf.readUInt32LE(i);
|
|
29262
|
-
if (i + 4 + len > end)
|
|
29263
|
-
break;
|
|
29264
|
-
if (code === 4)
|
|
29265
|
-
errorName = buf.toString("utf8", i + 4, i + 4 + len);
|
|
29266
|
-
i += 4 + len + 1;
|
|
29267
|
-
} else if (sig === "g") {
|
|
29268
|
-
const len = buf[i];
|
|
29269
|
-
i += 1 + len + 1;
|
|
29270
|
-
} else {
|
|
29271
|
-
break;
|
|
29272
|
-
}
|
|
29273
|
-
}
|
|
29274
|
-
return { type: buf[1], replySerial, errorName, bodyStart, bodyLength, total };
|
|
29275
|
-
}
|
|
29276
|
-
function resolveBusPath(env) {
|
|
29277
|
-
const declared = env.DBUS_SESSION_BUS_ADDRESS;
|
|
29278
|
-
if (declared) {
|
|
29279
|
-
for (const part of declared.split(";")) {
|
|
29280
|
-
const m = /^unix:(?:.*,)?path=([^,]+)/.exec(part.trim());
|
|
29281
|
-
if (m)
|
|
29282
|
-
return m[1];
|
|
29283
|
-
}
|
|
29284
|
-
}
|
|
29285
|
-
const uid = typeof process.getuid === "function" ? process.getuid() : -1;
|
|
29286
|
-
return uid >= 0 ? `/run/user/${uid}/bus` : null;
|
|
29287
|
-
}
|
|
29288
|
-
function notifyDesktop(summary, body, replacesId = 0, opts = {}) {
|
|
29289
|
-
return new Promise((resolvePromise) => {
|
|
29290
|
-
if (process.platform !== "linux" && !opts.socketPath) {
|
|
29291
|
-
resolvePromise({ ok: false, reason: "error", detail: "linux only" });
|
|
29292
|
-
return;
|
|
29293
|
-
}
|
|
29294
|
-
const uid = typeof process.getuid === "function" ? process.getuid() : -1;
|
|
29295
|
-
const path = opts.socketPath ?? resolveBusPath(process.env);
|
|
29296
|
-
if (!path || uid < 0) {
|
|
29297
|
-
resolvePromise({ ok: false, reason: "no-session", detail: "no bus address" });
|
|
29298
|
-
return;
|
|
29299
|
-
}
|
|
29300
|
-
let settled = false;
|
|
29301
|
-
const done = (o) => {
|
|
29302
|
-
if (settled)
|
|
29303
|
-
return;
|
|
29304
|
-
settled = true;
|
|
29305
|
-
clearTimeout(timer);
|
|
29306
|
-
try {
|
|
29307
|
-
sock.destroy();
|
|
29308
|
-
} catch {}
|
|
29309
|
-
resolvePromise(o);
|
|
29310
|
-
};
|
|
29311
|
-
const timer = setTimeout(() => done({ ok: false, reason: "timeout", detail: `${TIMEOUT_MS}ms` }), TIMEOUT_MS);
|
|
29312
|
-
let sock;
|
|
29313
|
-
try {
|
|
29314
|
-
sock = connect(path);
|
|
29315
|
-
} catch (err) {
|
|
29316
|
-
done({ ok: false, reason: "no-session", detail: String(err) });
|
|
29317
|
-
return;
|
|
29318
|
-
}
|
|
29319
|
-
let stage = "auth";
|
|
29320
|
-
let buf = Buffer.alloc(0);
|
|
29321
|
-
sock.on("error", (err) => {
|
|
29322
|
-
done({
|
|
29323
|
-
ok: false,
|
|
29324
|
-
reason: err.code === "ENOENT" || err.code === "ECONNREFUSED" ? "no-session" : "error",
|
|
29325
|
-
detail: err.code ?? String(err)
|
|
29326
|
-
});
|
|
29327
|
-
});
|
|
29328
|
-
sock.on("close", () => {
|
|
29329
|
-
done({ ok: false, reason: "refused", detail: `bus closed the connection during ${stage}` });
|
|
29330
|
-
});
|
|
29331
|
-
sock.on("connect", () => {
|
|
29332
|
-
const hexUid = Buffer.from(String(uid), "utf8").toString("hex");
|
|
29333
|
-
sock.write(Buffer.concat([Buffer.from([0]), Buffer.from(`AUTH EXTERNAL ${hexUid}\r
|
|
29334
|
-
`, "utf8")]));
|
|
29335
|
-
});
|
|
29336
|
-
sock.on("data", (chunk) => {
|
|
29337
|
-
buf = Buffer.concat([buf, chunk]);
|
|
29338
|
-
if (stage === "auth") {
|
|
29339
|
-
const text = buf.toString("latin1");
|
|
29340
|
-
const nl = text.indexOf(`\r
|
|
29341
|
-
`);
|
|
29342
|
-
if (nl === -1)
|
|
29343
|
-
return;
|
|
29344
|
-
if (!text.startsWith("OK")) {
|
|
29345
|
-
done({ ok: false, reason: "refused", detail: text.slice(0, nl).trim().slice(0, 80) });
|
|
29346
|
-
return;
|
|
29347
|
-
}
|
|
29348
|
-
buf = buf.subarray(nl + 2);
|
|
29349
|
-
stage = "hello";
|
|
29350
|
-
sock.write(Buffer.from(`BEGIN\r
|
|
29351
|
-
`, "utf8"));
|
|
29352
|
-
sock.write(methodCall(1, HELLO_FIELDS, Buffer.alloc(0)));
|
|
29353
|
-
}
|
|
29354
|
-
for (;; ) {
|
|
29355
|
-
const msg = readMessage(buf);
|
|
29356
|
-
if (!msg)
|
|
29357
|
-
return;
|
|
29358
|
-
const frame = buf.subarray(0, msg.total);
|
|
29359
|
-
buf = buf.subarray(msg.total);
|
|
29360
|
-
if (msg.type === 4 || msg.type === 1)
|
|
29361
|
-
continue;
|
|
29362
|
-
if (stage === "hello") {
|
|
29363
|
-
if (msg.type === 3) {
|
|
29364
|
-
done({ ok: false, reason: "refused", detail: msg.errorName ?? "Hello failed" });
|
|
29365
|
-
return;
|
|
29366
|
-
}
|
|
29367
|
-
if (msg.replySerial !== 1)
|
|
29368
|
-
continue;
|
|
29369
|
-
stage = "notify";
|
|
29370
|
-
sock.write(methodCall(2, NOTIFY_FIELDS, notifyBody("failproofai", replacesId, summary, body)));
|
|
29371
|
-
continue;
|
|
29372
|
-
}
|
|
29373
|
-
if (msg.replySerial !== 2)
|
|
29374
|
-
continue;
|
|
29375
|
-
if (msg.type === 3) {
|
|
29376
|
-
const name = msg.errorName ?? "";
|
|
29377
|
-
const noServer = name.includes("ServiceUnknown") || name.includes("NameHasNoOwner");
|
|
29378
|
-
done({ ok: false, reason: noServer ? "no-server" : "error", detail: name || "error" });
|
|
29379
|
-
return;
|
|
29380
|
-
}
|
|
29381
|
-
const id = msg.bodyLength >= 4 && frame.length >= msg.bodyStart + 4 ? frame.readUInt32LE(msg.bodyStart) : 0;
|
|
29382
|
-
done({ ok: true, id });
|
|
29383
|
-
return;
|
|
29384
|
-
}
|
|
29385
|
-
});
|
|
29386
|
-
});
|
|
29387
|
-
}
|
|
29388
|
-
var TIMEOUT_MS = 2000, HELLO_FIELDS, NOTIFY_FIELDS;
|
|
29389
|
-
var init_desktop_notify = __esm(() => {
|
|
29390
|
-
HELLO_FIELDS = [
|
|
29391
|
-
[1, "o", "/org/freedesktop/DBus"],
|
|
29392
|
-
[6, "s", "org.freedesktop.DBus"],
|
|
29393
|
-
[2, "s", "org.freedesktop.DBus"],
|
|
29394
|
-
[3, "s", "Hello"]
|
|
29395
|
-
];
|
|
29396
|
-
NOTIFY_FIELDS = [
|
|
29397
|
-
[1, "o", "/org/freedesktop/Notifications"],
|
|
29398
|
-
[6, "s", "org.freedesktop.Notifications"],
|
|
29399
|
-
[2, "s", "org.freedesktop.Notifications"],
|
|
29400
|
-
[3, "s", "Notify"],
|
|
29401
|
-
[8, "g", "susssasa{sv}i"]
|
|
29402
|
-
];
|
|
29403
|
-
});
|
|
29404
|
-
|
|
29405
|
-
// src/hooks/notice.ts
|
|
29406
|
-
function desktopLeakNotice(email = "held") {
|
|
29407
|
-
const followUp = email === "sent" ? "A masked alert was also sent to your email." : email === "missing" ? "Add email alerts with: failproofai audit --schedule --email you@example.com" : email === "failed" ? "Email delivery failed this time; the local result is still available." : "Email was not sent for this run.";
|
|
29408
|
-
return {
|
|
29409
|
-
title: "failproofai audit needs review",
|
|
29410
|
-
body: "Possible credential exposure was found in your agent transcripts. " + `Run failproofai audit to verify the matches and rotate anything real. ${followUp}`
|
|
29411
|
-
};
|
|
29412
|
-
}
|
|
29413
|
-
|
|
29414
28250
|
// src/audit/cli.ts
|
|
29415
28251
|
function colorOn() {
|
|
29416
28252
|
if (process.env.NO_COLOR && process.env.NO_COLOR !== "")
|
|
@@ -29601,10 +28437,6 @@ var init_cli = __esm(() => {
|
|
|
29601
28437
|
init_telemetry_sanitize();
|
|
29602
28438
|
init_open_browser();
|
|
29603
28439
|
init_report_harm();
|
|
29604
|
-
init_desktop_notify();
|
|
29605
|
-
init_macos_notifier();
|
|
29606
|
-
init_fp_config();
|
|
29607
|
-
init_auth_store();
|
|
29608
28440
|
init_tui();
|
|
29609
28441
|
init_package();
|
|
29610
28442
|
AUDIT_STAGES = [
|
|
@@ -29623,16 +28455,16 @@ var init_cli = __esm(() => {
|
|
|
29623
28455
|
});
|
|
29624
28456
|
|
|
29625
28457
|
// src/hooks/configure-wizard.ts
|
|
29626
|
-
import { existsSync as
|
|
29627
|
-
import { homedir as
|
|
29628
|
-
import { dirname as
|
|
28458
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync21, writeFileSync as writeFileSync19, readFileSync as readFileSync30 } from "node:fs";
|
|
28459
|
+
import { homedir as homedir27 } from "node:os";
|
|
28460
|
+
import { dirname as dirname16, resolve as resolve24, sep as sep5 } from "node:path";
|
|
29629
28461
|
async function emit(event, props) {
|
|
29630
28462
|
try {
|
|
29631
28463
|
await trackHookEvent2(getInstanceId2(), event, props);
|
|
29632
28464
|
} catch {}
|
|
29633
28465
|
}
|
|
29634
28466
|
function homeify(p) {
|
|
29635
|
-
const home =
|
|
28467
|
+
const home = homedir27();
|
|
29636
28468
|
if (p === home)
|
|
29637
28469
|
return "~";
|
|
29638
28470
|
if (p.startsWith(home + sep5))
|
|
@@ -29648,8 +28480,8 @@ function setCustomPoliciesEnabled(scope, cwd, enabled) {
|
|
|
29648
28480
|
const path = getConfigPathForScope(scope, cwd);
|
|
29649
28481
|
let config = {};
|
|
29650
28482
|
try {
|
|
29651
|
-
if (
|
|
29652
|
-
config = JSON.parse(
|
|
28483
|
+
if (existsSync34(path))
|
|
28484
|
+
config = JSON.parse(readFileSync30(path, "utf8"));
|
|
29653
28485
|
} catch {
|
|
29654
28486
|
return;
|
|
29655
28487
|
}
|
|
@@ -29658,7 +28490,7 @@ function setCustomPoliciesEnabled(scope, cwd, enabled) {
|
|
|
29658
28490
|
else
|
|
29659
28491
|
config.customPoliciesEnabled = false;
|
|
29660
28492
|
try {
|
|
29661
|
-
|
|
28493
|
+
writeFileSync19(path, JSON.stringify(config, null, 2) + `
|
|
29662
28494
|
`, "utf8");
|
|
29663
28495
|
} catch {}
|
|
29664
28496
|
}
|
|
@@ -29689,7 +28521,7 @@ function describeCustomPolicies(cwd) {
|
|
|
29689
28521
|
const scopes = [];
|
|
29690
28522
|
let fileCount = 0;
|
|
29691
28523
|
const dirs = [
|
|
29692
|
-
{ dir:
|
|
28524
|
+
{ dir: resolve24(cwd, ".failproofai", "policies"), label: "project" },
|
|
29693
28525
|
{ dir: customPoliciesDir(), label: "global" }
|
|
29694
28526
|
];
|
|
29695
28527
|
for (const { dir, label } of dirs) {
|
|
@@ -29700,7 +28532,7 @@ function describeCustomPolicies(cwd) {
|
|
|
29700
28532
|
fileCount += found.length;
|
|
29701
28533
|
}
|
|
29702
28534
|
for (const name of findSkippedPolicyFiles(dir)) {
|
|
29703
|
-
warnings.push(`! ${homeify(
|
|
28535
|
+
warnings.push(`! ${homeify(resolve24(dir, name))} is NOT loaded — rename to ` + `${name.replace(/\.(js|mjs|ts)$/, "-policies.$1")}`);
|
|
29704
28536
|
}
|
|
29705
28537
|
}
|
|
29706
28538
|
return { active, warnings, fileCount, scopes };
|
|
@@ -29793,8 +28625,8 @@ function firstRunMarkerPath() {
|
|
|
29793
28625
|
}
|
|
29794
28626
|
function markLauncherSeen() {
|
|
29795
28627
|
try {
|
|
29796
|
-
|
|
29797
|
-
|
|
28628
|
+
mkdirSync21(dirname16(firstRunMarkerPath()), { recursive: true });
|
|
28629
|
+
writeFileSync19(firstRunMarkerPath(), "1", "utf8");
|
|
29798
28630
|
} catch {}
|
|
29799
28631
|
}
|
|
29800
28632
|
function retryProbe() {
|
|
@@ -29872,8 +28704,6 @@ async function runConfigureWizard(io = {}, answers = {}) {
|
|
|
29872
28704
|
const cwd = process.cwd();
|
|
29873
28705
|
const unattended = !stdin.isTTY || !stdout.isTTY;
|
|
29874
28706
|
const preAnswered = Boolean(answers.token);
|
|
29875
|
-
const initialAudit = readConfigRaw().raw.audit;
|
|
29876
|
-
const hadExplicitAuditAuto = !!initialAudit && typeof initialAudit === "object" && !Array.isArray(initialAudit) && typeof initialAudit.auto === "boolean";
|
|
29877
28707
|
if (typeof process.getuid === "function" && process.getuid() === 0 && process.env.SUDO_USER) {
|
|
29878
28708
|
stdout.write(`Run failproofai config as ${process.env.SUDO_USER}, not under sudo.
|
|
29879
28709
|
` + `Everything it configures is per-user — hooks, policies and the daemon's own
|
|
@@ -30196,21 +29026,7 @@ Hooks keep enforcing; scheduled audits stay off until it is.
|
|
|
30196
29026
|
}
|
|
30197
29027
|
if (daemonInstalled) {
|
|
30198
29028
|
setDaemonConfigured(true, version2);
|
|
30199
|
-
if (!hadExplicitAuditAuto) {
|
|
30200
|
-
try {
|
|
30201
|
-
updateConfig({ audit: { auto: true } });
|
|
30202
|
-
} catch {}
|
|
30203
|
-
}
|
|
30204
29029
|
pruneOldDaemonBinaries();
|
|
30205
|
-
const notifier = installMacNotifier();
|
|
30206
|
-
if (process.platform === "darwin" && !notifier.installed) {
|
|
30207
|
-
hookLogWarn(`macOS notification helper was not installed: ${notifier.reason ?? "unknown error"}`);
|
|
30208
|
-
stdout.write(`
|
|
30209
|
-
System notifications could not be installed. Scheduled scans and email alerts
|
|
30210
|
-
still work; re-run \`failproofai config\` after checking the local LaunchAgent.
|
|
30211
|
-
|
|
30212
|
-
`);
|
|
30213
|
-
}
|
|
30214
29030
|
}
|
|
30215
29031
|
const applied = emit("configure_applied", {
|
|
30216
29032
|
target,
|
|
@@ -30288,7 +29104,6 @@ var init_configure_wizard = __esm(() => {
|
|
|
30288
29104
|
init_hook_telemetry2();
|
|
30289
29105
|
init_telemetry_id2();
|
|
30290
29106
|
init_daemon_service();
|
|
30291
|
-
init_macos_notifier();
|
|
30292
29107
|
init_hook_logger();
|
|
30293
29108
|
init_cloud_enrollment();
|
|
30294
29109
|
init_cloud_connection();
|
|
@@ -30298,22 +29113,21 @@ var init_configure_wizard = __esm(() => {
|
|
|
30298
29113
|
init_package();
|
|
30299
29114
|
init_onboarding_attempt();
|
|
30300
29115
|
init_onboarding_lock();
|
|
30301
|
-
init_fp_config();
|
|
30302
29116
|
POLICY_LIST_INDENT = " ".repeat(15);
|
|
30303
29117
|
});
|
|
30304
29118
|
|
|
30305
29119
|
// src/hooks/flush-cli.ts
|
|
30306
|
-
import { existsSync as
|
|
30307
|
-
import { dirname as
|
|
29120
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync22, writeFileSync as writeFileSync20, readdirSync as readdirSync18 } from "node:fs";
|
|
29121
|
+
import { dirname as dirname17, join as join35 } from "node:path";
|
|
30308
29122
|
function flushRequestPath2(home) {
|
|
30309
29123
|
return join35(failproofaiHome(home), "state", "flush-request.json");
|
|
30310
29124
|
}
|
|
30311
29125
|
function spoolDirs2(home) {
|
|
30312
29126
|
const root = spoolDir(home);
|
|
30313
|
-
if (!
|
|
29127
|
+
if (!existsSync35(root))
|
|
30314
29128
|
return [];
|
|
30315
29129
|
try {
|
|
30316
|
-
return
|
|
29130
|
+
return readdirSync18(root, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name !== "failed").map((e) => join35(root, e.name));
|
|
30317
29131
|
} catch {
|
|
30318
29132
|
return [];
|
|
30319
29133
|
}
|
|
@@ -30322,7 +29136,7 @@ function pendingBatches2(home) {
|
|
|
30322
29136
|
let n = 0;
|
|
30323
29137
|
for (const dir of spoolDirs2(home)) {
|
|
30324
29138
|
try {
|
|
30325
|
-
n +=
|
|
29139
|
+
n += readdirSync18(dir).filter((f) => f.endsWith(".jsonl")).length;
|
|
30326
29140
|
} catch {}
|
|
30327
29141
|
}
|
|
30328
29142
|
return n;
|
|
@@ -30379,8 +29193,8 @@ async function runFlushCommand2(opts = {}) {
|
|
|
30379
29193
|
return { exitCode: 0, pending: 0, lines: ["Nothing spooled — everything already delivered."] };
|
|
30380
29194
|
}
|
|
30381
29195
|
const path = flushRequestPath2(home);
|
|
30382
|
-
|
|
30383
|
-
|
|
29196
|
+
mkdirSync22(dirname17(path), { recursive: true });
|
|
29197
|
+
writeFileSync20(path, `${JSON.stringify({ requestedAtMs: Date.now() }, null, 2)}
|
|
30384
29198
|
`, {
|
|
30385
29199
|
mode: 384
|
|
30386
29200
|
});
|
|
@@ -30411,8 +29225,8 @@ var init_flush_cli2 = __esm(() => {
|
|
|
30411
29225
|
});
|
|
30412
29226
|
|
|
30413
29227
|
// src/hooks/backfill-cli.ts
|
|
30414
|
-
import { existsSync as
|
|
30415
|
-
import { dirname as
|
|
29228
|
+
import { existsSync as existsSync36, mkdirSync as mkdirSync23, writeFileSync as writeFileSync21, readdirSync as readdirSync19, statSync as statSync23 } from "node:fs";
|
|
29229
|
+
import { dirname as dirname18, join as join36 } from "node:path";
|
|
30416
29230
|
function backfillRequestPath(home) {
|
|
30417
29231
|
return join36(failproofaiHome(home), "state", "backfill-request.json");
|
|
30418
29232
|
}
|
|
@@ -30420,7 +29234,7 @@ function surveySessions(sinceMs, homeDir) {
|
|
|
30420
29234
|
const out = [];
|
|
30421
29235
|
for (const { cli, dir } of SESSION_DIRS) {
|
|
30422
29236
|
const root = join36(homeDir, dir);
|
|
30423
|
-
if (!
|
|
29237
|
+
if (!existsSync36(root))
|
|
30424
29238
|
continue;
|
|
30425
29239
|
let files = 0;
|
|
30426
29240
|
let newest = 0;
|
|
@@ -30429,7 +29243,7 @@ function surveySessions(sinceMs, homeDir) {
|
|
|
30429
29243
|
return;
|
|
30430
29244
|
let entries;
|
|
30431
29245
|
try {
|
|
30432
|
-
entries =
|
|
29246
|
+
entries = readdirSync19(d, { withFileTypes: true });
|
|
30433
29247
|
} catch {
|
|
30434
29248
|
return;
|
|
30435
29249
|
}
|
|
@@ -30458,7 +29272,7 @@ function runBackfillCommand(opts = {}) {
|
|
|
30458
29272
|
const now = opts.now ?? Date.now();
|
|
30459
29273
|
const sinceMs = opts.sinceMs ?? now - DEFAULT_BACKFILL_DAYS * 24 * 60 * 60 * 1000;
|
|
30460
29274
|
const home = failproofaiHome();
|
|
30461
|
-
if (!
|
|
29275
|
+
if (!existsSync36(home)) {
|
|
30462
29276
|
return {
|
|
30463
29277
|
exitCode: 1,
|
|
30464
29278
|
lines: [`No failproofai home at ${home}. Run \`failproofai config\` first.`]
|
|
@@ -30490,7 +29304,7 @@ function runBackfillCommand(opts = {}) {
|
|
|
30490
29304
|
]
|
|
30491
29305
|
};
|
|
30492
29306
|
}
|
|
30493
|
-
const found = surveySessions(sinceMs,
|
|
29307
|
+
const found = surveySessions(sinceMs, dirname18(home));
|
|
30494
29308
|
const totalFiles = found.reduce((n, f) => n + f.files, 0);
|
|
30495
29309
|
const sinceLabel = new Date(sinceMs).toISOString().slice(0, 10);
|
|
30496
29310
|
const lines = [
|
|
@@ -30513,8 +29327,8 @@ function runBackfillCommand(opts = {}) {
|
|
|
30513
29327
|
}
|
|
30514
29328
|
try {
|
|
30515
29329
|
const path = backfillRequestPath();
|
|
30516
|
-
|
|
30517
|
-
|
|
29330
|
+
mkdirSync23(dirname18(path), { recursive: true });
|
|
29331
|
+
writeFileSync21(path, `${JSON.stringify({ sinceMs, requestedAtMs: now }, null, 2)}
|
|
30518
29332
|
`, {
|
|
30519
29333
|
mode: 384
|
|
30520
29334
|
});
|
|
@@ -30556,8 +29370,8 @@ var init_backfill_cli = __esm(() => {
|
|
|
30556
29370
|
});
|
|
30557
29371
|
|
|
30558
29372
|
// src/hooks/harness-cli.ts
|
|
30559
|
-
import { homedir as
|
|
30560
|
-
import { resolve as
|
|
29373
|
+
import { homedir as homedir28 } from "node:os";
|
|
29374
|
+
import { resolve as resolve25 } from "node:path";
|
|
30561
29375
|
function unknownHarness(name) {
|
|
30562
29376
|
return fail([
|
|
30563
29377
|
`Unknown harness: ${name}`,
|
|
@@ -30600,8 +29414,8 @@ function comparableLabel(raw) {
|
|
|
30600
29414
|
return raw.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
30601
29415
|
}
|
|
30602
29416
|
function comparablePath(raw) {
|
|
30603
|
-
const home =
|
|
30604
|
-
const expanded = raw === "~" ? home : raw.startsWith("~/") ?
|
|
29417
|
+
const home = homedir28();
|
|
29418
|
+
const expanded = raw === "~" ? home : raw.startsWith("~/") ? resolve25(home, raw.slice(2)) : raw;
|
|
30605
29419
|
return expanded.replace(/\/+$/, "");
|
|
30606
29420
|
}
|
|
30607
29421
|
function effectiveLabel(entry) {
|
|
@@ -30789,7 +29603,7 @@ var init_harness_cli = __esm(() => {
|
|
|
30789
29603
|
});
|
|
30790
29604
|
|
|
30791
29605
|
// src/hooks/fp-config.ts
|
|
30792
|
-
import { chmodSync as
|
|
29606
|
+
import { chmodSync as chmodSync6, existsSync as existsSync37, mkdirSync as mkdirSync24, readFileSync as readFileSync31, statSync as statSync24, writeFileSync as writeFileSync22 } from "node:fs";
|
|
30793
29607
|
function detectLayout2() {
|
|
30794
29608
|
const raw = readVersionFile2();
|
|
30795
29609
|
if (raw) {
|
|
@@ -30797,17 +29611,17 @@ function detectLayout2() {
|
|
|
30797
29611
|
return { kind: "current", version: raw };
|
|
30798
29612
|
return raw.layout > LAYOUT_VERSION ? { kind: "future", found: raw.layout } : { kind: "stale", found: raw.layout };
|
|
30799
29613
|
}
|
|
30800
|
-
if (
|
|
29614
|
+
if (existsSync37(configFile())) {
|
|
30801
29615
|
const layoutThreePositions = [
|
|
30802
29616
|
legacy.authJson(),
|
|
30803
29617
|
legacy.nextAudit(),
|
|
30804
29618
|
legacy.auditSchedule()
|
|
30805
29619
|
];
|
|
30806
|
-
if (layoutThreePositions.some((p) =>
|
|
29620
|
+
if (layoutThreePositions.some((p) => existsSync37(p)))
|
|
30807
29621
|
return { kind: "stale", found: 3 };
|
|
30808
29622
|
return { kind: "current", version: { layout: LAYOUT_VERSION, cli: "" }, inferred: true };
|
|
30809
29623
|
}
|
|
30810
|
-
if (
|
|
29624
|
+
if (existsSync37(legacy.configToml()))
|
|
30811
29625
|
return { kind: "stale", found: 2 };
|
|
30812
29626
|
const layoutOneMarkers = [
|
|
30813
29627
|
legacy.policyConfig(),
|
|
@@ -30818,7 +29632,7 @@ function detectLayout2() {
|
|
|
30818
29632
|
legacy.ingestCredentials(),
|
|
30819
29633
|
legacy.auditDashboard()
|
|
30820
29634
|
];
|
|
30821
|
-
return layoutOneMarkers.some((p) =>
|
|
29635
|
+
return layoutOneMarkers.some((p) => existsSync37(p)) ? { kind: "stale", found: 1 } : { kind: "absent" };
|
|
30822
29636
|
}
|
|
30823
29637
|
function readLegacyTomlVersion2(text) {
|
|
30824
29638
|
const layout = Number(/^\s*layout\s*=\s*(\d+)\s*$/m.exec(text)?.[1]);
|
|
@@ -30830,7 +29644,7 @@ function readLegacyTomlVersion2(text) {
|
|
|
30830
29644
|
function readVersionFile2() {
|
|
30831
29645
|
let text;
|
|
30832
29646
|
try {
|
|
30833
|
-
text =
|
|
29647
|
+
text = readFileSync31(versionFile(), "utf8");
|
|
30834
29648
|
} catch {
|
|
30835
29649
|
return null;
|
|
30836
29650
|
}
|
|
@@ -30859,16 +29673,16 @@ var init_fp_home2 = () => {};
|
|
|
30859
29673
|
|
|
30860
29674
|
// src/hooks/migrations.ts
|
|
30861
29675
|
import {
|
|
30862
|
-
chmodSync as
|
|
29676
|
+
chmodSync as chmodSync7,
|
|
30863
29677
|
copyFileSync as copyFileSync3,
|
|
30864
|
-
existsSync as
|
|
30865
|
-
mkdirSync as
|
|
30866
|
-
readFileSync as
|
|
30867
|
-
renameSync as
|
|
30868
|
-
rmSync as
|
|
30869
|
-
writeFileSync as
|
|
29678
|
+
existsSync as existsSync38,
|
|
29679
|
+
mkdirSync as mkdirSync25,
|
|
29680
|
+
readFileSync as readFileSync32,
|
|
29681
|
+
renameSync as renameSync8,
|
|
29682
|
+
rmSync as rmSync14,
|
|
29683
|
+
writeFileSync as writeFileSync23
|
|
30870
29684
|
} from "node:fs";
|
|
30871
|
-
import { basename as basename7, dirname as
|
|
29685
|
+
import { basename as basename7, dirname as dirname19, join as join37, resolve as resolve26 } from "node:path";
|
|
30872
29686
|
function migrateToLayout42() {
|
|
30873
29687
|
const moves = [
|
|
30874
29688
|
{ from: legacy.authJson(), to: auditSessionFile() },
|
|
@@ -30884,33 +29698,33 @@ function migrateToLayout42() {
|
|
|
30884
29698
|
}
|
|
30885
29699
|
const migrated = [];
|
|
30886
29700
|
for (const { from, to } of moves) {
|
|
30887
|
-
if (!
|
|
29701
|
+
if (!existsSync38(from))
|
|
30888
29702
|
continue;
|
|
30889
|
-
if (
|
|
30890
|
-
|
|
29703
|
+
if (existsSync38(to)) {
|
|
29704
|
+
rmSync14(from, { force: true });
|
|
30891
29705
|
continue;
|
|
30892
29706
|
}
|
|
30893
|
-
|
|
29707
|
+
mkdirSync25(dirname19(to), { recursive: true });
|
|
30894
29708
|
try {
|
|
30895
|
-
|
|
29709
|
+
renameSync8(from, to);
|
|
30896
29710
|
} catch {
|
|
30897
29711
|
try {
|
|
30898
29712
|
copyFileSync3(from, to);
|
|
30899
29713
|
} catch (err) {
|
|
30900
29714
|
try {
|
|
30901
|
-
|
|
29715
|
+
rmSync14(to, { force: true });
|
|
30902
29716
|
} catch {}
|
|
30903
29717
|
throw err;
|
|
30904
29718
|
}
|
|
30905
|
-
|
|
29719
|
+
rmSync14(from, { force: true });
|
|
30906
29720
|
}
|
|
30907
29721
|
migrated.push(`${basename7(from)} → audit/${basename7(to)}`);
|
|
30908
29722
|
}
|
|
30909
29723
|
for (const secret of [auditSessionFile()]) {
|
|
30910
|
-
if (!
|
|
29724
|
+
if (!existsSync38(secret))
|
|
30911
29725
|
continue;
|
|
30912
29726
|
try {
|
|
30913
|
-
|
|
29727
|
+
chmodSync7(secret, 384);
|
|
30914
29728
|
} catch {}
|
|
30915
29729
|
}
|
|
30916
29730
|
writeVersionFile({ layout: 4 });
|
|
@@ -30933,7 +29747,7 @@ function planMigration2(from, registry = MIGRATIONS2) {
|
|
|
30933
29747
|
}
|
|
30934
29748
|
function readLedger2() {
|
|
30935
29749
|
try {
|
|
30936
|
-
const parsed = JSON.parse(
|
|
29750
|
+
const parsed = JSON.parse(readFileSync32(migrationLedgerFile(), "utf8"));
|
|
30937
29751
|
return Array.isArray(parsed) ? parsed : [];
|
|
30938
29752
|
} catch {
|
|
30939
29753
|
return [];
|
|
@@ -30941,9 +29755,9 @@ function readLedger2() {
|
|
|
30941
29755
|
}
|
|
30942
29756
|
function appendLedger2(entry) {
|
|
30943
29757
|
try {
|
|
30944
|
-
|
|
29758
|
+
mkdirSync25(migrationsDir(), { recursive: true });
|
|
30945
29759
|
const all = [...readLedger2(), entry];
|
|
30946
|
-
|
|
29760
|
+
writeFileSync23(migrationLedgerFile(), `${JSON.stringify(all, null, 2)}
|
|
30947
29761
|
`, "utf8");
|
|
30948
29762
|
} catch {}
|
|
30949
29763
|
}
|
|
@@ -30955,12 +29769,12 @@ function pruneMigratedCredentials2(from) {
|
|
|
30955
29769
|
for (let layout = from;layout < LAYOUT_VERSION; layout++) {
|
|
30956
29770
|
for (const { as, landsAt } of PRUNED_AFTER_SUCCESS2) {
|
|
30957
29771
|
try {
|
|
30958
|
-
if (!
|
|
29772
|
+
if (!existsSync38(landsAt()))
|
|
30959
29773
|
continue;
|
|
30960
|
-
const copy =
|
|
30961
|
-
if (!
|
|
29774
|
+
const copy = resolve26(migrationBackupDir(layout), as);
|
|
29775
|
+
if (!existsSync38(copy))
|
|
30962
29776
|
continue;
|
|
30963
|
-
|
|
29777
|
+
rmSync14(copy, { force: true });
|
|
30964
29778
|
pruned.push(as);
|
|
30965
29779
|
} catch {}
|
|
30966
29780
|
}
|
|
@@ -30971,7 +29785,7 @@ function filesToBackUp2() {
|
|
|
30971
29785
|
const seen = new Map;
|
|
30972
29786
|
for (const f of [...BACKED_UP2, ...BACKED_UP_LEGACY2]) {
|
|
30973
29787
|
const path = f.at();
|
|
30974
|
-
if (
|
|
29788
|
+
if (existsSync38(path))
|
|
30975
29789
|
seen.set(path, backupNameOf2(f));
|
|
30976
29790
|
}
|
|
30977
29791
|
return [...seen].map(([path, as]) => ({ path, as }));
|
|
@@ -30980,14 +29794,14 @@ function backupBeforeMigrating2(from) {
|
|
|
30980
29794
|
const dir = migrationBackupDir(from);
|
|
30981
29795
|
const saved = [];
|
|
30982
29796
|
try {
|
|
30983
|
-
|
|
30984
|
-
|
|
29797
|
+
rmSync14(dir, { recursive: true, force: true });
|
|
29798
|
+
mkdirSync25(dir, { recursive: true });
|
|
30985
29799
|
} catch {
|
|
30986
29800
|
return [];
|
|
30987
29801
|
}
|
|
30988
29802
|
for (const { path: source, as } of filesToBackUp2()) {
|
|
30989
29803
|
try {
|
|
30990
|
-
copyFileSync3(source,
|
|
29804
|
+
copyFileSync3(source, resolve26(dir, as));
|
|
30991
29805
|
saved.push(as);
|
|
30992
29806
|
} catch {}
|
|
30993
29807
|
}
|
|
@@ -31125,17 +29939,17 @@ var init_migrations2 = __esm(() => {
|
|
|
31125
29939
|
|
|
31126
29940
|
// src/hooks/daemon-service.ts
|
|
31127
29941
|
import {
|
|
31128
|
-
existsSync as
|
|
31129
|
-
mkdirSync as
|
|
29942
|
+
existsSync as existsSync39,
|
|
29943
|
+
mkdirSync as mkdirSync26,
|
|
31130
29944
|
mkdtempSync as mkdtempSync2,
|
|
31131
|
-
readFileSync as
|
|
31132
|
-
writeFileSync as
|
|
29945
|
+
readFileSync as readFileSync33,
|
|
29946
|
+
writeFileSync as writeFileSync24,
|
|
31133
29947
|
unlinkSync as unlinkSync5,
|
|
31134
|
-
rmSync as
|
|
29948
|
+
rmSync as rmSync15
|
|
31135
29949
|
} from "node:fs";
|
|
31136
|
-
import { homedir as
|
|
31137
|
-
import { resolve as
|
|
31138
|
-
import { execFileSync as
|
|
29950
|
+
import { homedir as homedir29, tmpdir as tmpdir3, userInfo as userInfo2 } from "node:os";
|
|
29951
|
+
import { resolve as resolve27 } from "node:path";
|
|
29952
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
31139
29953
|
function isDaemonSupportedPlatform2() {
|
|
31140
29954
|
return process.platform === "linux" || process.platform === "darwin";
|
|
31141
29955
|
}
|
|
@@ -31150,13 +29964,13 @@ function resolveFailproofaidBinaryPath2() {
|
|
|
31150
29964
|
if (process.env.FAILPROOFAI_DAEMON_BINARY)
|
|
31151
29965
|
return process.env.FAILPROOFAI_DAEMON_BINARY;
|
|
31152
29966
|
const downloaded = installedBinaryPath();
|
|
31153
|
-
if (
|
|
29967
|
+
if (existsSync39(downloaded))
|
|
31154
29968
|
return downloaded;
|
|
31155
29969
|
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT;
|
|
31156
29970
|
if (packageRoot) {
|
|
31157
29971
|
for (const profile of ["release", "debug"]) {
|
|
31158
|
-
const candidate =
|
|
31159
|
-
if (
|
|
29972
|
+
const candidate = resolve27(packageRoot, "target", profile, "failproofaid");
|
|
29973
|
+
if (existsSync39(candidate))
|
|
31160
29974
|
return candidate;
|
|
31161
29975
|
}
|
|
31162
29976
|
}
|
|
@@ -31186,8 +30000,8 @@ function resolveWorkerCommand2() {
|
|
|
31186
30000
|
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT;
|
|
31187
30001
|
if (!packageRoot)
|
|
31188
30002
|
return null;
|
|
31189
|
-
const workerScript =
|
|
31190
|
-
if (!
|
|
30003
|
+
const workerScript = resolve27(packageRoot, "dist", "worker.mjs");
|
|
30004
|
+
if (!existsSync39(workerScript))
|
|
31191
30005
|
return null;
|
|
31192
30006
|
return `${shellQuote2(process.execPath)} ${shellQuote2(workerScript)}`;
|
|
31193
30007
|
}
|
|
@@ -31197,8 +30011,8 @@ function resolveCliCommand2() {
|
|
|
31197
30011
|
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT;
|
|
31198
30012
|
if (!packageRoot)
|
|
31199
30013
|
return null;
|
|
31200
|
-
const cliScript =
|
|
31201
|
-
if (!
|
|
30014
|
+
const cliScript = resolve27(packageRoot, "dist", "cli.mjs");
|
|
30015
|
+
if (!existsSync39(cliScript))
|
|
31202
30016
|
return null;
|
|
31203
30017
|
return `${shellQuote2(process.execPath)} ${shellQuote2(cliScript)}`;
|
|
31204
30018
|
}
|
|
@@ -31211,8 +30025,8 @@ function workerScriptPath2() {
|
|
|
31211
30025
|
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT;
|
|
31212
30026
|
if (!packageRoot)
|
|
31213
30027
|
return null;
|
|
31214
|
-
const workerScript =
|
|
31215
|
-
return
|
|
30028
|
+
const workerScript = resolve27(packageRoot, "dist", "worker.mjs");
|
|
30029
|
+
return existsSync39(workerScript) ? workerScript : null;
|
|
31216
30030
|
}
|
|
31217
30031
|
function assertUnitSafe2(value, field) {
|
|
31218
30032
|
if (UNIT_UNSAFE_RE2.test(value)) {
|
|
@@ -31238,10 +30052,10 @@ function systemdUnitDir2() {
|
|
|
31238
30052
|
return process.env.FAILPROOFAI_SYSTEMD_DIR || "/etc/systemd/system";
|
|
31239
30053
|
}
|
|
31240
30054
|
function systemdUnitPath2(user = serviceUser2()) {
|
|
31241
|
-
return
|
|
30055
|
+
return resolve27(systemdUnitDir2(), systemdUnitName2(user));
|
|
31242
30056
|
}
|
|
31243
30057
|
function legacySystemdUserUnitPath2() {
|
|
31244
|
-
return
|
|
30058
|
+
return resolve27(homedir29(), ".config", "systemd", "user", "failproofaid.service");
|
|
31245
30059
|
}
|
|
31246
30060
|
function launchdLabel2(user = serviceUser2()) {
|
|
31247
30061
|
return `ai.failproof.failproofaid.${user}`;
|
|
@@ -31250,7 +30064,7 @@ function launchdPlistPath2(user = serviceUser2()) {
|
|
|
31250
30064
|
return `/Library/LaunchDaemons/${launchdLabel2(user)}.plist`;
|
|
31251
30065
|
}
|
|
31252
30066
|
function legacyLaunchAgentPlistPath2() {
|
|
31253
|
-
return
|
|
30067
|
+
return resolve27(homedir29(), "Library", "LaunchAgents", "ai.failproof.failproofaid.plist");
|
|
31254
30068
|
}
|
|
31255
30069
|
function legacySharedLaunchDaemonPath2() {
|
|
31256
30070
|
return "/Library/LaunchDaemons/ai.failproof.failproofaid.plist";
|
|
@@ -31266,7 +30080,7 @@ function primeElevation2() {
|
|
|
31266
30080
|
if (canElevate2())
|
|
31267
30081
|
return true;
|
|
31268
30082
|
try {
|
|
31269
|
-
|
|
30083
|
+
execFileSync3("sudo", ["-v"], { stdio: "inherit", timeout: SUDO_PROMPT_TIMEOUT_MS2 });
|
|
31270
30084
|
return canElevate2();
|
|
31271
30085
|
} catch {
|
|
31272
30086
|
return false;
|
|
@@ -31276,7 +30090,7 @@ function canElevate2() {
|
|
|
31276
30090
|
if (typeof process.getuid === "function" && process.getuid() === 0)
|
|
31277
30091
|
return true;
|
|
31278
30092
|
try {
|
|
31279
|
-
|
|
30093
|
+
execFileSync3("sudo", ["-n", "true"], { stdio: "ignore", timeout: SERVICE_CMD_TIMEOUT_MS2 });
|
|
31280
30094
|
return true;
|
|
31281
30095
|
} catch {
|
|
31282
30096
|
return false;
|
|
@@ -31285,42 +30099,42 @@ function canElevate2() {
|
|
|
31285
30099
|
function runPrivileged2(command, args) {
|
|
31286
30100
|
const root = typeof process.getuid === "function" && process.getuid() === 0;
|
|
31287
30101
|
const [cmd, argv] = root ? [command, args] : ["sudo", ["-n", command, ...args]];
|
|
31288
|
-
|
|
30102
|
+
execFileSync3(cmd, argv, { stdio: "ignore", timeout: SERVICE_CMD_TIMEOUT_MS2 });
|
|
31289
30103
|
}
|
|
31290
30104
|
function writePrivilegedFile2(destination, contents, mode = "0644") {
|
|
31291
|
-
const stagingDir = mkdtempSync2(
|
|
31292
|
-
const staging =
|
|
30105
|
+
const stagingDir = mkdtempSync2(resolve27(tmpdir3(), "failproofaid-stage-"));
|
|
30106
|
+
const staging = resolve27(stagingDir, "service-definition");
|
|
31293
30107
|
try {
|
|
31294
|
-
|
|
30108
|
+
writeFileSync24(staging, contents, "utf8");
|
|
31295
30109
|
runPrivileged2("install", ["-m", mode, staging, destination]);
|
|
31296
30110
|
} finally {
|
|
31297
|
-
|
|
30111
|
+
rmSync15(stagingDir, { recursive: true, force: true });
|
|
31298
30112
|
}
|
|
31299
30113
|
}
|
|
31300
30114
|
function removeLegacyUserService2() {
|
|
31301
30115
|
try {
|
|
31302
30116
|
if (process.platform === "linux") {
|
|
31303
30117
|
const legacy = legacySystemdUserUnitPath2();
|
|
31304
|
-
if (!
|
|
30118
|
+
if (!existsSync39(legacy))
|
|
31305
30119
|
return;
|
|
31306
30120
|
try {
|
|
31307
|
-
|
|
30121
|
+
execFileSync3("systemctl", ["--user", "disable", "--now", "failproofaid.service"], {
|
|
31308
30122
|
stdio: "ignore",
|
|
31309
30123
|
timeout: SERVICE_CMD_TIMEOUT_MS2
|
|
31310
30124
|
});
|
|
31311
30125
|
} catch {}
|
|
31312
30126
|
unlinkSync5(legacy);
|
|
31313
30127
|
try {
|
|
31314
|
-
|
|
30128
|
+
execFileSync3("systemctl", ["--user", "daemon-reload"], {
|
|
31315
30129
|
stdio: "ignore",
|
|
31316
30130
|
timeout: SERVICE_CMD_TIMEOUT_MS2
|
|
31317
30131
|
});
|
|
31318
30132
|
} catch {}
|
|
31319
30133
|
} else {
|
|
31320
30134
|
const legacyAgent = legacyLaunchAgentPlistPath2();
|
|
31321
|
-
if (
|
|
30135
|
+
if (existsSync39(legacyAgent)) {
|
|
31322
30136
|
try {
|
|
31323
|
-
|
|
30137
|
+
execFileSync3("launchctl", ["unload", "-w", legacyAgent], {
|
|
31324
30138
|
stdio: "ignore",
|
|
31325
30139
|
timeout: SERVICE_CMD_TIMEOUT_MS2
|
|
31326
30140
|
});
|
|
@@ -31328,7 +30142,7 @@ function removeLegacyUserService2() {
|
|
|
31328
30142
|
unlinkSync5(legacyAgent);
|
|
31329
30143
|
}
|
|
31330
30144
|
const legacyDaemon = legacySharedLaunchDaemonPath2();
|
|
31331
|
-
if (
|
|
30145
|
+
if (existsSync39(legacyDaemon)) {
|
|
31332
30146
|
try {
|
|
31333
30147
|
runPrivileged2("launchctl", ["unload", "-w", legacyDaemon]);
|
|
31334
30148
|
} catch {}
|
|
@@ -31344,8 +30158,8 @@ function systemdUnitContents2(binaryPath, workerCmd, cliCmd = null) {
|
|
|
31344
30158
|
`).join("");
|
|
31345
30159
|
const user = assertUnitSafe2(serviceUser2(), "User");
|
|
31346
30160
|
assertUnitSafe2(binaryPath, "ExecStart");
|
|
31347
|
-
assertUnitSafe2(
|
|
31348
|
-
const conditionPaths = [binaryPath, workerScriptPath2()].filter((p) => typeof p === "string" &&
|
|
30161
|
+
assertUnitSafe2(homedir29(), "HOME");
|
|
30162
|
+
const conditionPaths = [binaryPath, workerScriptPath2()].filter((p) => typeof p === "string" && existsSync39(p));
|
|
31349
30163
|
const conditionLines = conditionPaths.map((p) => `ConditionPathExists=${assertUnitSafe2(p, "ConditionPathExists")}
|
|
31350
30164
|
`).join("");
|
|
31351
30165
|
return `[Unit]
|
|
@@ -31358,7 +30172,7 @@ User=${user}
|
|
|
31358
30172
|
# Set explicitly rather than relying on systemd deriving it from User=:
|
|
31359
30173
|
# failproofaid stores per-user state and refuses to start without HOME, so this
|
|
31360
30174
|
# required variable is not left to a version-dependent default.
|
|
31361
|
-
Environment="HOME=${
|
|
30175
|
+
Environment="HOME=${homedir29()}"
|
|
31362
30176
|
${envLines}ExecStart=${binaryPath}
|
|
31363
30177
|
Restart=on-failure
|
|
31364
30178
|
RestartSec=2
|
|
@@ -31370,7 +30184,7 @@ RestartSec=2
|
|
|
31370
30184
|
WantedBy=multi-user.target
|
|
31371
30185
|
`;
|
|
31372
30186
|
}
|
|
31373
|
-
function
|
|
30187
|
+
function escapeXml2(s) {
|
|
31374
30188
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
31375
30189
|
}
|
|
31376
30190
|
function launchdPlistContents2(binaryPath, logDir, workerCmd, cliCmd = null) {
|
|
@@ -31378,7 +30192,7 @@ function launchdPlistContents2(binaryPath, logDir, workerCmd, cliCmd = null) {
|
|
|
31378
30192
|
const envBlock = env.length ? ` <key>EnvironmentVariables</key>
|
|
31379
30193
|
<dict>
|
|
31380
30194
|
${env.map(([key, value]) => ` <key>${key}</key>
|
|
31381
|
-
<string>${
|
|
30195
|
+
<string>${escapeXml2(value)}</string>`).join(`
|
|
31382
30196
|
`)}
|
|
31383
30197
|
</dict>
|
|
31384
30198
|
` : "";
|
|
@@ -31387,10 +30201,10 @@ ${env.map(([key, value]) => ` <key>${key}</key>
|
|
|
31387
30201
|
<plist version="1.0">
|
|
31388
30202
|
<dict>
|
|
31389
30203
|
<key>Label</key>
|
|
31390
|
-
<string>${
|
|
30204
|
+
<string>${escapeXml2(launchdLabel2())}</string>
|
|
31391
30205
|
<key>ProgramArguments</key>
|
|
31392
30206
|
<array>
|
|
31393
|
-
<string>${
|
|
30207
|
+
<string>${escapeXml2(binaryPath)}</string>
|
|
31394
30208
|
</array>
|
|
31395
30209
|
<key>RunAtLoad</key>
|
|
31396
30210
|
<true/>
|
|
@@ -31401,11 +30215,11 @@ ${env.map(([key, value]) => ` <key>${key}</key>
|
|
|
31401
30215
|
system, executed as the one user whose home, socket and lock it
|
|
31402
30216
|
is allowed to touch. -->
|
|
31403
30217
|
<key>UserName</key>
|
|
31404
|
-
<string>${
|
|
30218
|
+
<string>${escapeXml2(serviceUser2())}</string>
|
|
31405
30219
|
${envBlock} <key>StandardOutPath</key>
|
|
31406
|
-
<string>${
|
|
30220
|
+
<string>${escapeXml2(resolve27(logDir, "failproofaid.log"))}</string>
|
|
31407
30221
|
<key>StandardErrorPath</key>
|
|
31408
|
-
<string>${
|
|
30222
|
+
<string>${escapeXml2(resolve27(logDir, "failproofaid.err.log"))}</string>
|
|
31409
30223
|
</dict>
|
|
31410
30224
|
</plist>
|
|
31411
30225
|
`;
|
|
@@ -31451,7 +30265,7 @@ async function installDaemonService2() {
|
|
|
31451
30265
|
} else {
|
|
31452
30266
|
const plistPath = launchdPlistPath2();
|
|
31453
30267
|
const logDir = logsDir();
|
|
31454
|
-
|
|
30268
|
+
mkdirSync26(logDir, { recursive: true });
|
|
31455
30269
|
try {
|
|
31456
30270
|
runPrivileged2("launchctl", ["unload", plistPath]);
|
|
31457
30271
|
} catch {}
|
|
@@ -31538,7 +30352,7 @@ async function refreshDaemonToCliVersion(deps = {}) {
|
|
|
31538
30352
|
}
|
|
31539
30353
|
function inactiveLinuxStatus2() {
|
|
31540
30354
|
try {
|
|
31541
|
-
return interpretConditionResult2(
|
|
30355
|
+
return interpretConditionResult2(execFileSync3("systemctl", ["show", systemdUnitName2(), "--property=ConditionResult", "--value"], { stdio: ["ignore", "pipe", "ignore"], timeout: SERVICE_CMD_TIMEOUT_MS2 }).toString());
|
|
31542
30356
|
} catch {
|
|
31543
30357
|
return "stopped";
|
|
31544
30358
|
}
|
|
@@ -31550,10 +30364,10 @@ function daemonServiceStatus2() {
|
|
|
31550
30364
|
if (!isDaemonSupportedPlatform2())
|
|
31551
30365
|
return "unsupported-platform";
|
|
31552
30366
|
if (process.platform === "linux") {
|
|
31553
|
-
if (!
|
|
30367
|
+
if (!existsSync39(systemdUnitPath2()))
|
|
31554
30368
|
return "not-installed";
|
|
31555
30369
|
try {
|
|
31556
|
-
const out =
|
|
30370
|
+
const out = execFileSync3("systemctl", ["is-active", systemdUnitName2()], {
|
|
31557
30371
|
stdio: ["ignore", "pipe", "ignore"],
|
|
31558
30372
|
timeout: SERVICE_CMD_TIMEOUT_MS2
|
|
31559
30373
|
}).toString().trim();
|
|
@@ -31563,17 +30377,17 @@ function daemonServiceStatus2() {
|
|
|
31563
30377
|
}
|
|
31564
30378
|
}
|
|
31565
30379
|
const plistPath = launchdPlistPath2();
|
|
31566
|
-
if (!
|
|
30380
|
+
if (!existsSync39(plistPath))
|
|
31567
30381
|
return "not-installed";
|
|
31568
30382
|
try {
|
|
31569
30383
|
const root = typeof process.getuid === "function" && process.getuid() === 0;
|
|
31570
30384
|
if (!root && !canElevate2())
|
|
31571
30385
|
return "unknown";
|
|
31572
30386
|
const args = ["print", `system/${launchdLabel2()}`];
|
|
31573
|
-
const out = root ?
|
|
30387
|
+
const out = root ? execFileSync3("launchctl", args, {
|
|
31574
30388
|
stdio: ["ignore", "pipe", "ignore"],
|
|
31575
30389
|
timeout: SERVICE_CMD_TIMEOUT_MS2
|
|
31576
|
-
}).toString() :
|
|
30390
|
+
}).toString() : execFileSync3("sudo", ["-n", "launchctl", ...args], {
|
|
31577
30391
|
stdio: ["ignore", "pipe", "ignore"],
|
|
31578
30392
|
timeout: SERVICE_CMD_TIMEOUT_MS2
|
|
31579
30393
|
}).toString();
|
|
@@ -31593,7 +30407,7 @@ var init_daemon_service2 = __esm(() => {
|
|
|
31593
30407
|
});
|
|
31594
30408
|
|
|
31595
30409
|
// src/hooks/uninstall-cli.ts
|
|
31596
|
-
import { existsSync as
|
|
30410
|
+
import { existsSync as existsSync40, rmSync as rmSync16 } from "node:fs";
|
|
31597
30411
|
function survey(cwd) {
|
|
31598
30412
|
const clis = [];
|
|
31599
30413
|
for (const id of listInstallableIds()) {
|
|
@@ -31614,9 +30428,9 @@ function survey(cwd) {
|
|
|
31614
30428
|
return {
|
|
31615
30429
|
clis,
|
|
31616
30430
|
servicePath,
|
|
31617
|
-
serviceInstalled: !!servicePath &&
|
|
30431
|
+
serviceInstalled: !!servicePath && existsSync40(servicePath),
|
|
31618
30432
|
daemonConfigured,
|
|
31619
|
-
homeExists:
|
|
30433
|
+
homeExists: existsSync40(failproofaiHome())
|
|
31620
30434
|
};
|
|
31621
30435
|
}
|
|
31622
30436
|
async function runUninstallCommand(opts = {}) {
|
|
@@ -31645,7 +30459,6 @@ async function runUninstallCommand(opts = {}) {
|
|
|
31645
30459
|
if (found.daemonConfigured) {
|
|
31646
30460
|
plan.push(` • stop requiring the daemon (policies go back to evaluating in-process)`);
|
|
31647
30461
|
}
|
|
31648
|
-
let removeDaemon = opts.purge === true;
|
|
31649
30462
|
if (found.serviceInstalled) {
|
|
31650
30463
|
plan.push(opts.purge || opts.yes ? ` • stop, disable and delete the service at ${found.servicePath} — needs sudo` : ` • ask whether to remove the service at ${found.servicePath} (kept unless you say so)`);
|
|
31651
30464
|
}
|
|
@@ -31705,7 +30518,7 @@ async function runUninstallCommand(opts = {}) {
|
|
|
31705
30518
|
}
|
|
31706
30519
|
}
|
|
31707
30520
|
if (found.serviceInstalled) {
|
|
31708
|
-
removeDaemon = opts.purge || opts.yes ? true : await opts.confirmDaemon?.() ?? false;
|
|
30521
|
+
const removeDaemon = opts.purge || opts.yes ? true : await opts.confirmDaemon?.() ?? false;
|
|
31709
30522
|
if (!removeDaemon) {
|
|
31710
30523
|
lines.push(`• kept the daemon service at ${found.servicePath}`, ` Remove it later with: failproofai uninstall --purge`);
|
|
31711
30524
|
} else {
|
|
@@ -31715,7 +30528,7 @@ async function runUninstallCommand(opts = {}) {
|
|
|
31715
30528
|
} catch {}
|
|
31716
30529
|
try {
|
|
31717
30530
|
await uninstallDaemonService();
|
|
31718
|
-
if (found.servicePath &&
|
|
30531
|
+
if (found.servicePath && existsSync40(found.servicePath)) {
|
|
31719
30532
|
failures.push(`the service at ${found.servicePath} is still there (most often: no sudo). ` + `Remove it with the commands below.`);
|
|
31720
30533
|
} else {
|
|
31721
30534
|
lines.push(`✓ stopped and removed the daemon service`);
|
|
@@ -31725,12 +30538,9 @@ async function runUninstallCommand(opts = {}) {
|
|
|
31725
30538
|
}
|
|
31726
30539
|
}
|
|
31727
30540
|
}
|
|
31728
|
-
if (opts.purge || removeDaemon) {
|
|
31729
|
-
uninstallMacNotifier();
|
|
31730
|
-
}
|
|
31731
30541
|
if (opts.purge && found.homeExists) {
|
|
31732
30542
|
try {
|
|
31733
|
-
|
|
30543
|
+
rmSync16(failproofaiHome(), { recursive: true, force: true });
|
|
31734
30544
|
purged = true;
|
|
31735
30545
|
lines.push(`✓ deleted ${failproofaiHome()}`);
|
|
31736
30546
|
} catch (err) {
|
|
@@ -31741,7 +30551,7 @@ async function runUninstallCommand(opts = {}) {
|
|
|
31741
30551
|
lines.push(``, `Not everything could be removed:`);
|
|
31742
30552
|
for (const f of failures)
|
|
31743
30553
|
lines.push(` ✗ ${f}`);
|
|
31744
|
-
if (found.servicePath &&
|
|
30554
|
+
if (found.servicePath && existsSync40(found.servicePath)) {
|
|
31745
30555
|
lines.push(``, `To remove the service by hand:`, ...manualServiceRemoval(found.servicePath), ``, `(\`${daemonStatusCommand()}\` shows its current state.)`);
|
|
31746
30556
|
}
|
|
31747
30557
|
return { exitCode: 1, lines, planLines, purged };
|
|
@@ -31769,15 +30579,14 @@ var init_uninstall_cli = __esm(() => {
|
|
|
31769
30579
|
init_daemon_service();
|
|
31770
30580
|
init_integrations();
|
|
31771
30581
|
init_manager();
|
|
31772
|
-
init_macos_notifier();
|
|
31773
30582
|
});
|
|
31774
30583
|
|
|
31775
30584
|
// src/hooks/pack-cli.ts
|
|
31776
|
-
import { execFileSync as
|
|
31777
|
-
import { homedir as
|
|
30585
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
30586
|
+
import { homedir as homedir30, tmpdir as tmpdir4 } from "node:os";
|
|
31778
30587
|
import { createHash as createHash9 } from "node:crypto";
|
|
31779
|
-
import { copyFileSync as copyFileSync4, existsSync as
|
|
31780
|
-
import { basename as basename8, dirname as
|
|
30588
|
+
import { copyFileSync as copyFileSync4, existsSync as existsSync41, mkdirSync as mkdirSync27, mkdtempSync as mkdtempSync3, readFileSync as readFileSync34, readdirSync as readdirSync20, realpathSync as realpathSync3, rmSync as rmSync17, statSync as statSync25, writeFileSync as writeFileSync25 } from "node:fs";
|
|
30589
|
+
import { basename as basename8, dirname as dirname20, isAbsolute as isAbsolute4, join as join38, relative as relative5, resolve as resolve28, sep as sep6 } from "node:path";
|
|
31781
30590
|
function parseList(rest, flag) {
|
|
31782
30591
|
const idx = rest.findIndex((a) => a === flag || a.startsWith(`${flag}=`));
|
|
31783
30592
|
if (idx === -1)
|
|
@@ -31877,7 +30686,7 @@ async function build(rest) {
|
|
|
31877
30686
|
const version = flag("version");
|
|
31878
30687
|
const commit = flag("commit");
|
|
31879
30688
|
const effect = flag("effect") ?? "enforce";
|
|
31880
|
-
const outDir =
|
|
30689
|
+
const outDir = resolve28(flag("out") ?? "dist-pack");
|
|
31881
30690
|
if (!entry || !id || !version) {
|
|
31882
30691
|
return fail2([
|
|
31883
30692
|
"Usage: failproofai publish <entry.mjs> --repo <owner>/<repo> --version <version>",
|
|
@@ -31890,10 +30699,10 @@ async function build(rest) {
|
|
|
31890
30699
|
} catch (err) {
|
|
31891
30700
|
return fail2([err instanceof Error ? err.message : String(err)]);
|
|
31892
30701
|
}
|
|
31893
|
-
const entryPath =
|
|
31894
|
-
if (!
|
|
30702
|
+
const entryPath = resolve28(entry);
|
|
30703
|
+
if (!existsSync41(entryPath))
|
|
31895
30704
|
return fail2([`No such file: ${entryPath}`]);
|
|
31896
|
-
const source =
|
|
30705
|
+
const source = readFileSync34(entryPath, "utf8");
|
|
31897
30706
|
const localImport = /(?:^|\n)\s*(?:import|export)[^;\n]*from\s+["'](\.[^"']*)["']/.exec(source);
|
|
31898
30707
|
if (localImport) {
|
|
31899
30708
|
return fail2([
|
|
@@ -31949,14 +30758,14 @@ async function build(rest) {
|
|
|
31949
30758
|
policies
|
|
31950
30759
|
}, null, 2) + `
|
|
31951
30760
|
`;
|
|
31952
|
-
|
|
31953
|
-
const manifestPath =
|
|
31954
|
-
const entryOut =
|
|
31955
|
-
|
|
30761
|
+
mkdirSync27(outDir, { recursive: true });
|
|
30762
|
+
const manifestPath = resolve28(outDir, PACK_MANIFEST_ASSET);
|
|
30763
|
+
const entryOut = resolve28(outDir, PACK_ENTRY_ASSET);
|
|
30764
|
+
writeFileSync25(manifestPath, manifest, "utf8");
|
|
31956
30765
|
copyFileSync4(entryPath, entryOut);
|
|
31957
30766
|
const sha = (bytes) => createHash9("sha256").update(bytes).digest("hex");
|
|
31958
|
-
|
|
31959
|
-
${sha(
|
|
30767
|
+
writeFileSync25(resolve28(outDir, PACK_CHECKSUMS_ASSET), `${sha(manifest)} ${PACK_MANIFEST_ASSET}
|
|
30768
|
+
${sha(readFileSync34(entryOut))} ${PACK_ENTRY_ASSET}
|
|
31960
30769
|
`, "utf8");
|
|
31961
30770
|
const on = policies.filter((p) => p.defaultEnabled).length;
|
|
31962
30771
|
return ok2([
|
|
@@ -32085,12 +30894,12 @@ async function scaffold(target) {
|
|
|
32085
30894
|
return ok2(["Nothing written."]);
|
|
32086
30895
|
chosen = answer.trim();
|
|
32087
30896
|
}
|
|
32088
|
-
const path =
|
|
32089
|
-
if (
|
|
30897
|
+
const path = resolve28(withPolicyExtension(chosen ?? "my-policies"));
|
|
30898
|
+
if (existsSync41(path)) {
|
|
32090
30899
|
return fail2([`${path} already exists — name a different file, or edit that one.`]);
|
|
32091
30900
|
}
|
|
32092
|
-
|
|
32093
|
-
|
|
30901
|
+
mkdirSync27(resolve28(path, ".."), { recursive: true });
|
|
30902
|
+
writeFileSync25(path, `import { customPolicies, allow, deny } from "failproofai";
|
|
32094
30903
|
|
|
32095
30904
|
// One file, no relative imports: only this entry is digest-pinned, so a pack
|
|
32096
30905
|
// that imported siblings could not honestly claim to be verified.
|
|
@@ -32126,8 +30935,8 @@ customPolicies.add({
|
|
|
32126
30935
|
}
|
|
32127
30936
|
function inferRepo(entryPath) {
|
|
32128
30937
|
try {
|
|
32129
|
-
const url =
|
|
32130
|
-
cwd:
|
|
30938
|
+
const url = execFileSync4("git", ["remote", "get-url", "origin"], {
|
|
30939
|
+
cwd: resolve28(entryPath, ".."),
|
|
32131
30940
|
encoding: "utf8",
|
|
32132
30941
|
stdio: ["ignore", "pipe", "ignore"],
|
|
32133
30942
|
timeout: 5000
|
|
@@ -32139,10 +30948,10 @@ function inferRepo(entryPath) {
|
|
|
32139
30948
|
}
|
|
32140
30949
|
}
|
|
32141
30950
|
function inferCommit(entryPath, outDir, sources) {
|
|
32142
|
-
const cwd =
|
|
30951
|
+
const cwd = resolve28(entryPath, "..");
|
|
32143
30952
|
const raw = (args) => {
|
|
32144
30953
|
try {
|
|
32145
|
-
return
|
|
30954
|
+
return execFileSync4("git", args, {
|
|
32146
30955
|
cwd,
|
|
32147
30956
|
encoding: "utf8",
|
|
32148
30957
|
stdio: ["ignore", "pipe", "ignore"],
|
|
@@ -32194,9 +31003,9 @@ function realOrSelf(path) {
|
|
|
32194
31003
|
}
|
|
32195
31004
|
}
|
|
32196
31005
|
function sourcesInHead(git, raw, sources) {
|
|
32197
|
-
const paths = sources.map((f) => realOrSelf(
|
|
31006
|
+
const paths = sources.map((f) => realOrSelf(resolve28(f)));
|
|
32198
31007
|
const shown = git(["rev-parse", "--show-toplevel"]);
|
|
32199
|
-
const top = shown === null ? null : realOrSelf(
|
|
31008
|
+
const top = shown === null ? null : realOrSelf(resolve28(shown));
|
|
32200
31009
|
const missing = [];
|
|
32201
31010
|
for (const path of paths) {
|
|
32202
31011
|
const rel = top ? relative5(top, path) : null;
|
|
@@ -32220,7 +31029,7 @@ function sourcesInHead(git, raw, sources) {
|
|
|
32220
31029
|
]);
|
|
32221
31030
|
if (status === null)
|
|
32222
31031
|
return { ok: false, offending: paths, why: "modified" };
|
|
32223
|
-
const dirty = porcelainEntries(status).flatMap((entry) => [entry.path, entry.from].filter(Boolean).map((path) =>
|
|
31032
|
+
const dirty = porcelainEntries(status).flatMap((entry) => [entry.path, entry.from].filter(Boolean).map((path) => resolve28(top ?? ".", path)));
|
|
32224
31033
|
if (dirty.length > 0)
|
|
32225
31034
|
return { ok: false, offending: dirty, why: "modified" };
|
|
32226
31035
|
return { ok: true };
|
|
@@ -32228,22 +31037,22 @@ function sourcesInHead(git, raw, sources) {
|
|
|
32228
31037
|
function skipOutDir(git, entryPath, outDir) {
|
|
32229
31038
|
if (!outDir)
|
|
32230
31039
|
return [];
|
|
32231
|
-
const out =
|
|
32232
|
-
if (
|
|
31040
|
+
const out = resolve28(outDir);
|
|
31041
|
+
if (resolve28(entryPath).startsWith(out + sep6))
|
|
32233
31042
|
return [];
|
|
32234
31043
|
const top = git(["rev-parse", "--show-toplevel"]);
|
|
32235
31044
|
if (!top)
|
|
32236
31045
|
return [];
|
|
32237
|
-
const rel = relative5(
|
|
31046
|
+
const rel = relative5(resolve28(top), out);
|
|
32238
31047
|
if (!rel || rel.startsWith("..") || isAbsolute4(rel))
|
|
32239
31048
|
return [];
|
|
32240
31049
|
return ["--", ":(top)", `:(exclude,literal,top)${rel.split(sep6).join("/")}`];
|
|
32241
31050
|
}
|
|
32242
31051
|
function inferTaggedVersion(entryPath, sources) {
|
|
32243
|
-
const cwd =
|
|
31052
|
+
const cwd = resolve28(entryPath, "..");
|
|
32244
31053
|
const raw = (args) => {
|
|
32245
31054
|
try {
|
|
32246
|
-
return
|
|
31055
|
+
return execFileSync4("git", args, {
|
|
32247
31056
|
cwd,
|
|
32248
31057
|
encoding: "utf8",
|
|
32249
31058
|
stdio: ["ignore", "pipe", "ignore"],
|
|
@@ -32270,7 +31079,7 @@ function inferTaggedVersion(entryPath, sources) {
|
|
|
32270
31079
|
function findEntry(dir) {
|
|
32271
31080
|
let names;
|
|
32272
31081
|
try {
|
|
32273
|
-
names =
|
|
31082
|
+
names = readdirSync20(dir);
|
|
32274
31083
|
} catch {
|
|
32275
31084
|
return [];
|
|
32276
31085
|
}
|
|
@@ -32278,11 +31087,11 @@ function findEntry(dir) {
|
|
|
32278
31087
|
for (const name of names) {
|
|
32279
31088
|
if (!/\.(mjs|js|ts)$/.test(name))
|
|
32280
31089
|
continue;
|
|
32281
|
-
const full =
|
|
31090
|
+
const full = resolve28(dir, name);
|
|
32282
31091
|
try {
|
|
32283
31092
|
if (statSync25(full).size > 512 * 1024)
|
|
32284
31093
|
continue;
|
|
32285
|
-
const text =
|
|
31094
|
+
const text = readFileSync34(full, "utf8");
|
|
32286
31095
|
if (/from\s+["']failproofai["']/.test(text) && /customPolicies\s*\.\s*add\s*\(/.test(text)) {
|
|
32287
31096
|
found.push(full);
|
|
32288
31097
|
}
|
|
@@ -32293,7 +31102,7 @@ function findEntry(dir) {
|
|
|
32293
31102
|
return found.sort();
|
|
32294
31103
|
}
|
|
32295
31104
|
function withLocalImports(sources) {
|
|
32296
|
-
const seen = new Set(sources.map((f) =>
|
|
31105
|
+
const seen = new Set(sources.map((f) => resolve28(f)));
|
|
32297
31106
|
const queue = [...seen];
|
|
32298
31107
|
while (queue.length > 0 && seen.size < 500) {
|
|
32299
31108
|
const file = queue.shift();
|
|
@@ -32301,13 +31110,13 @@ function withLocalImports(sources) {
|
|
|
32301
31110
|
try {
|
|
32302
31111
|
if (statSync25(file).size > 512 * 1024)
|
|
32303
31112
|
continue;
|
|
32304
|
-
text =
|
|
31113
|
+
text = readFileSync34(file, "utf8");
|
|
32305
31114
|
} catch {
|
|
32306
31115
|
continue;
|
|
32307
31116
|
}
|
|
32308
31117
|
for (const re of LOCAL_IMPORT_RES) {
|
|
32309
31118
|
for (const match of text.matchAll(re)) {
|
|
32310
|
-
const base =
|
|
31119
|
+
const base = resolve28(dirname20(file), match[1]);
|
|
32311
31120
|
for (const ext of LOCAL_IMPORT_EXTENSIONS) {
|
|
32312
31121
|
const candidate = base + ext;
|
|
32313
31122
|
if (seen.has(candidate))
|
|
@@ -32328,10 +31137,10 @@ function withLocalImports(sources) {
|
|
|
32328
31137
|
return [...seen];
|
|
32329
31138
|
}
|
|
32330
31139
|
function settleGitState(entryPath, provenance, packFiles, id, outDir) {
|
|
32331
|
-
const cwd =
|
|
31140
|
+
const cwd = resolve28(entryPath, "..");
|
|
32332
31141
|
const raw = (args) => {
|
|
32333
31142
|
try {
|
|
32334
|
-
return
|
|
31143
|
+
return execFileSync4("git", args, {
|
|
32335
31144
|
cwd,
|
|
32336
31145
|
encoding: "utf8",
|
|
32337
31146
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -32403,11 +31212,11 @@ function settleGitState(entryPath, provenance, packFiles, id, outDir) {
|
|
|
32403
31212
|
if (root === null || status === null)
|
|
32404
31213
|
return null;
|
|
32405
31214
|
const entries = porcelainEntries(status);
|
|
32406
|
-
const dirty = entries.map((entry) =>
|
|
32407
|
-
const renamedFrom = entries.filter((entry) => entry.from).map((entry) =>
|
|
31215
|
+
const dirty = entries.map((entry) => resolve28(root, entry.path));
|
|
31216
|
+
const renamedFrom = entries.filter((entry) => entry.from).map((entry) => resolve28(root, entry.from));
|
|
32408
31217
|
if (dirty.length === 0)
|
|
32409
31218
|
return null;
|
|
32410
|
-
const mine = new Set(packFiles.map((f) =>
|
|
31219
|
+
const mine = new Set(packFiles.map((f) => resolve28(f)));
|
|
32411
31220
|
const foreign = dirty.filter((path) => !mine.has(path));
|
|
32412
31221
|
const toStage = [...dirty, ...renamedFrom];
|
|
32413
31222
|
if (foreign.length > 0) {
|
|
@@ -32524,7 +31333,7 @@ async function ensureRepo(owner, name, id, token, sourceDir) {
|
|
|
32524
31333
|
function pushExistingHistory(dir, owner, name, token) {
|
|
32525
31334
|
const git = (args, env) => {
|
|
32526
31335
|
try {
|
|
32527
|
-
return
|
|
31336
|
+
return execFileSync4("git", args, {
|
|
32528
31337
|
cwd: dir,
|
|
32529
31338
|
encoding: "utf8",
|
|
32530
31339
|
stdio: ["ignore", "pipe", "ignore"],
|
|
@@ -32540,7 +31349,7 @@ function pushExistingHistory(dir, owner, name, token) {
|
|
|
32540
31349
|
return false;
|
|
32541
31350
|
const askpass = join38(mkdtempSync3(join38(tmpdir4(), "fpai-askpass-")), "askpass.sh");
|
|
32542
31351
|
try {
|
|
32543
|
-
|
|
31352
|
+
writeFileSync25(askpass, `#!/bin/sh
|
|
32544
31353
|
case "$1" in
|
|
32545
31354
|
*Username*) echo x-access-token ;;
|
|
32546
31355
|
*) echo "$FPAI_GH_TOKEN" ;;
|
|
@@ -32560,7 +31369,7 @@ esac
|
|
|
32560
31369
|
return true;
|
|
32561
31370
|
} finally {
|
|
32562
31371
|
try {
|
|
32563
|
-
|
|
31372
|
+
rmSync17(dirname20(askpass), { recursive: true, force: true });
|
|
32564
31373
|
} catch {}
|
|
32565
31374
|
}
|
|
32566
31375
|
}
|
|
@@ -32584,14 +31393,14 @@ failproofai policies add ${owner}/${name}
|
|
|
32584
31393
|
function findBun() {
|
|
32585
31394
|
const candidates = [
|
|
32586
31395
|
"bun",
|
|
32587
|
-
process.env.BUN_INSTALL ?
|
|
32588
|
-
|
|
31396
|
+
process.env.BUN_INSTALL ? resolve28(process.env.BUN_INSTALL, "bin", "bun") : null,
|
|
31397
|
+
resolve28(homedir30(), ".bun", "bin", "bun"),
|
|
32589
31398
|
"/opt/homebrew/bin/bun",
|
|
32590
31399
|
"/usr/local/bin/bun"
|
|
32591
31400
|
].filter((c) => c !== null);
|
|
32592
31401
|
for (const candidate of candidates) {
|
|
32593
31402
|
try {
|
|
32594
|
-
|
|
31403
|
+
execFileSync4(candidate, ["--version"], { stdio: "ignore", timeout: 5000 });
|
|
32595
31404
|
return candidate;
|
|
32596
31405
|
} catch {
|
|
32597
31406
|
continue;
|
|
@@ -32611,14 +31420,14 @@ function bundleEntry(sources, outDir) {
|
|
|
32611
31420
|
]
|
|
32612
31421
|
};
|
|
32613
31422
|
}
|
|
32614
|
-
|
|
32615
|
-
const entry =
|
|
32616
|
-
|
|
31423
|
+
mkdirSync27(outDir, { recursive: true });
|
|
31424
|
+
const entry = resolve28(outDir, ".entry.generated.mjs");
|
|
31425
|
+
writeFileSync25(entry, sources.map((f) => `import ${JSON.stringify(f.startsWith("/") ? f : resolve28(f))};`).join(`
|
|
32617
31426
|
`) + `
|
|
32618
31427
|
`, "utf8");
|
|
32619
|
-
const bundled =
|
|
31428
|
+
const bundled = resolve28(outDir, ".bundled.mjs");
|
|
32620
31429
|
try {
|
|
32621
|
-
|
|
31430
|
+
execFileSync4(bun, [
|
|
32622
31431
|
"build",
|
|
32623
31432
|
"--target=node",
|
|
32624
31433
|
"--format=esm",
|
|
@@ -32648,7 +31457,7 @@ function githubToken() {
|
|
|
32648
31457
|
if (fromEnv)
|
|
32649
31458
|
return fromEnv.trim();
|
|
32650
31459
|
try {
|
|
32651
|
-
const out =
|
|
31460
|
+
const out = execFileSync4("gh", ["auth", "token"], {
|
|
32652
31461
|
encoding: "utf8",
|
|
32653
31462
|
stdio: ["ignore", "pipe", "ignore"],
|
|
32654
31463
|
timeout: 5000
|
|
@@ -32734,7 +31543,7 @@ async function publish(rest) {
|
|
|
32734
31543
|
`);
|
|
32735
31544
|
const token = githubToken();
|
|
32736
31545
|
const login = token ? await credentialLogin(token) : null;
|
|
32737
|
-
const folder = basename8(
|
|
31546
|
+
const folder = basename8(resolve28(entry, ".."));
|
|
32738
31547
|
const answered = await askWhereToPublish(`${login ?? "your-account"}/${folder}`, { stdin: process.stdin, stdout: process.stdout });
|
|
32739
31548
|
if (answered === null)
|
|
32740
31549
|
return ok2(["Nothing was published."]);
|
|
@@ -32745,7 +31554,7 @@ async function publish(rest) {
|
|
|
32745
31554
|
let versionFromSha = false;
|
|
32746
31555
|
const dryRun = rest.includes("--dry-run") || !repo;
|
|
32747
31556
|
const allowPrivate = rest.includes("--allow-private");
|
|
32748
|
-
const id = flag("id") ?? repo ?? (dryRun && entry ? `local/${basename8(
|
|
31557
|
+
const id = flag("id") ?? repo ?? (dryRun && entry ? `local/${basename8(resolve28(entry, ".."))}` : undefined);
|
|
32749
31558
|
const [owner, name] = (repo ?? "/").split("/");
|
|
32750
31559
|
if (repo && (!owner || !name || repo.split("/").length !== 2)) {
|
|
32751
31560
|
return fail2([`--repo must be <owner>/<repo>, got ${JSON.stringify(repo)}`]);
|
|
@@ -32764,7 +31573,7 @@ async function publish(rest) {
|
|
|
32764
31573
|
"With no --repo it writes the three release assets and stops."
|
|
32765
31574
|
]);
|
|
32766
31575
|
}
|
|
32767
|
-
const outDirEarly =
|
|
31576
|
+
const outDirEarly = resolve28(flag("out") ?? "dist-pack");
|
|
32768
31577
|
let provenance = entry ? inferCommit(entry, outDirEarly, bundledSources) : null;
|
|
32769
31578
|
const gitLines = [];
|
|
32770
31579
|
if (!version && entry && interactive) {
|
|
@@ -32804,7 +31613,7 @@ async function publish(rest) {
|
|
|
32804
31613
|
"It needs write access to releases on that repository, and nothing else."
|
|
32805
31614
|
]);
|
|
32806
31615
|
}
|
|
32807
|
-
const ensured = await ensureRepo(owner, name, id, token,
|
|
31616
|
+
const ensured = await ensureRepo(owner, name, id, token, resolve28(entry, ".."));
|
|
32808
31617
|
if ("error" in ensured)
|
|
32809
31618
|
return fail2(ensured.error);
|
|
32810
31619
|
created = ensured.created;
|
|
@@ -32824,7 +31633,7 @@ async function publish(rest) {
|
|
|
32824
31633
|
}
|
|
32825
31634
|
const tag = flag("tag") ?? version;
|
|
32826
31635
|
let entryToBuild = entry;
|
|
32827
|
-
const needsBundle = discovered.length > 1 || /(?:^|\n)\s*(?:import|export)[^;\n]*from\s+["']\.[^"']*["']/.test(
|
|
31636
|
+
const needsBundle = discovered.length > 1 || /(?:^|\n)\s*(?:import|export)[^;\n]*from\s+["']\.[^"']*["']/.test(readFileSync34(entry, "utf8"));
|
|
32828
31637
|
if (needsBundle) {
|
|
32829
31638
|
const sources = discovered.length > 1 ? discovered : [entry];
|
|
32830
31639
|
const bundled = bundleEntry(sources, outDirEarly);
|
|
@@ -32903,7 +31712,7 @@ async function publish(rest) {
|
|
|
32903
31712
|
if (prior) {
|
|
32904
31713
|
await gh(`${GITHUB_API2}/repos/${owner}/${name}/releases/assets/${prior.id}`, auth, { method: "DELETE" });
|
|
32905
31714
|
}
|
|
32906
|
-
const bytes =
|
|
31715
|
+
const bytes = readFileSync34(resolve28(outDir, asset));
|
|
32907
31716
|
const upload = await gh(`${GITHUB_UPLOADS2}/repos/${owner}/${name}/releases/${releaseId}/assets?name=${encodeURIComponent(asset)}`, auth, {
|
|
32908
31717
|
method: "POST",
|
|
32909
31718
|
contentType: asset.endsWith(".json") ? "application/json" : "application/octet-stream",
|
|
@@ -33584,8 +32393,8 @@ var init_install_prompt = __esm(() => {
|
|
|
33584
32393
|
|
|
33585
32394
|
// src/hooks/manager.ts
|
|
33586
32395
|
import { execSync as execSync7 } from "node:child_process";
|
|
33587
|
-
import { existsSync as
|
|
33588
|
-
import { resolve as
|
|
32396
|
+
import { existsSync as existsSync42 } from "node:fs";
|
|
32397
|
+
import { resolve as resolve29, basename as basename9 } from "node:path";
|
|
33589
32398
|
import { platform as platform4, arch as arch4, release as release3, hostname as hostname6 } from "node:os";
|
|
33590
32399
|
function getSettingsPath2(scope, cwd) {
|
|
33591
32400
|
return claudeCode.getSettingsPath(scope, cwd);
|
|
@@ -33804,9 +32613,9 @@ async function installHooksImpl2(policyNames, scope = "user", cwd, includeBeta =
|
|
|
33804
32613
|
delete configToWrite.customPoliciesPath;
|
|
33805
32614
|
delete configToWrite.customPoliciesPaths;
|
|
33806
32615
|
} else if (customPoliciesPath && (typeof customPoliciesPath === "string" || customPoliciesPath.length > 0)) {
|
|
33807
|
-
const incoming = (typeof customPoliciesPath === "string" ? [customPoliciesPath] : customPoliciesPath).map((path) =>
|
|
33808
|
-
const carried = (previousConfig.customPoliciesPaths ?? (previousConfig.customPoliciesPath ? [previousConfig.customPoliciesPath] : [])).map((path) =>
|
|
33809
|
-
if (
|
|
32616
|
+
const incoming = (typeof customPoliciesPath === "string" ? [customPoliciesPath] : customPoliciesPath).map((path) => resolve29(path));
|
|
32617
|
+
const carried = (previousConfig.customPoliciesPaths ?? (previousConfig.customPoliciesPath ? [previousConfig.customPoliciesPath] : [])).map((path) => resolve29(path)).filter((path) => {
|
|
32618
|
+
if (existsSync42(path))
|
|
33810
32619
|
return true;
|
|
33811
32620
|
console.log(`Dropping custom policies path (file no longer exists): ${path}`);
|
|
33812
32621
|
return false;
|
|
@@ -34029,7 +32838,7 @@ async function removeHooks2(policyNames, scope = "user", cwd, opts) {
|
|
|
34029
32838
|
const scopesToRemove = scope === "all" ? [...integration.scopes] : integration.scopes.includes(scope) ? [scope] : [];
|
|
34030
32839
|
for (const s of scopesToRemove) {
|
|
34031
32840
|
const settingsPaths = settingsPathsFor(integration, s, cwd);
|
|
34032
|
-
const existing = settingsPaths.filter((p) =>
|
|
32841
|
+
const existing = settingsPaths.filter((p) => existsSync42(p));
|
|
34033
32842
|
if (existing.length === 0) {
|
|
34034
32843
|
if (scope !== "all" && selectedClis.length === 1) {
|
|
34035
32844
|
console.log("No settings file found. Nothing to remove.");
|
|
@@ -34173,9 +32982,9 @@ async function listHooks(cwd) {
|
|
|
34173
32982
|
if (explicitPaths.length > 0) {
|
|
34174
32983
|
groups.push(rule("Custom Policies", opts));
|
|
34175
32984
|
for (const path of explicitPaths) {
|
|
34176
|
-
const absPath =
|
|
32985
|
+
const absPath = resolve29(findProjectConfigDir(cwd ?? process.cwd()), path);
|
|
34177
32986
|
groups.push(note(absPath, opts));
|
|
34178
|
-
if (!
|
|
32987
|
+
if (!existsSync42(absPath)) {
|
|
34179
32988
|
groups.push(warning([`file not found: ${absPath}`], opts));
|
|
34180
32989
|
continue;
|
|
34181
32990
|
}
|
|
@@ -34195,7 +33004,7 @@ async function listHooks(cwd) {
|
|
|
34195
33004
|
}
|
|
34196
33005
|
}
|
|
34197
33006
|
const base = findProjectConfigDir(cwd ?? process.cwd());
|
|
34198
|
-
const projectDir =
|
|
33007
|
+
const projectDir = resolve29(base, ".failproofai", "policies");
|
|
34199
33008
|
const userDir = customPoliciesDir();
|
|
34200
33009
|
const sameDir = userDir === projectDir;
|
|
34201
33010
|
const conventionDirs = [
|
|
@@ -34307,11 +33116,11 @@ var init_manager2 = __esm(() => {
|
|
|
34307
33116
|
});
|
|
34308
33117
|
|
|
34309
33118
|
// src/audit/audit-schedule.ts
|
|
34310
|
-
import { readFileSync as
|
|
33119
|
+
import { readFileSync as readFileSync35 } from "node:fs";
|
|
34311
33120
|
function readAuditSchedule(home) {
|
|
34312
33121
|
let raw;
|
|
34313
33122
|
try {
|
|
34314
|
-
raw =
|
|
33123
|
+
raw = readFileSync35(auditScheduleFile(home), "utf8");
|
|
34315
33124
|
} catch {
|
|
34316
33125
|
return null;
|
|
34317
33126
|
}
|
|
@@ -34375,9 +33184,7 @@ async function ensureSignedIn(preset) {
|
|
|
34375
33184
|
}
|
|
34376
33185
|
if (!canPrompt()) {
|
|
34377
33186
|
throw new LoginError(`Signing in needs an interactive terminal, and this one is not.
|
|
34378
|
-
` + "Run `failproofai audit --schedule` from a shell you are sitting at,\n" +
|
|
34379
|
-
` + `
|
|
34380
|
-
` + "`--email` does not avoid this: it fills in the address so you are not\n" + "asked for it, but a one-time code is still sent there for you to paste.");
|
|
33187
|
+
` + "Run `failproofai audit --schedule` from a shell you are sitting at,\n" + "or sign in from the dashboard — both write the same session file.");
|
|
34381
33188
|
}
|
|
34382
33189
|
return { user: await runLogin(preset), prompted: true };
|
|
34383
33190
|
}
|
|
@@ -34501,20 +33308,21 @@ async function runScheduleOn(daysArg, emailArg) {
|
|
|
34501
33308
|
if (bad)
|
|
34502
33309
|
throw new ScheduleCliError(bad);
|
|
34503
33310
|
}
|
|
34504
|
-
const
|
|
33311
|
+
const { user, prompted } = await ensureSignedIn(emailArg);
|
|
34505
33312
|
const next = updateConfig({
|
|
34506
33313
|
audit: {
|
|
34507
33314
|
auto: true,
|
|
33315
|
+
reportsConsentedAt: Date.now(),
|
|
34508
33316
|
...days !== undefined ? { intervalDays: days } : {}
|
|
34509
33317
|
}
|
|
34510
33318
|
});
|
|
34511
33319
|
const interval = next.audit.intervalDays;
|
|
34512
33320
|
const summary = [
|
|
34513
|
-
`every ${interval} day${interval === 1 ? "" : "s"} ·
|
|
34514
|
-
|
|
34515
|
-
"
|
|
33321
|
+
`every ${interval} day${interval === 1 ? "" : "s"} · reports to ${user.email}`,
|
|
33322
|
+
"you only hear from it when a scan finds something harmful",
|
|
33323
|
+
"each report sends: finding counts, redacted example commands, this machine's name"
|
|
34516
33324
|
];
|
|
34517
|
-
if (
|
|
33325
|
+
if (prompted) {
|
|
34518
33326
|
step("scheduled audits are on", summary);
|
|
34519
33327
|
} else {
|
|
34520
33328
|
process.stdout.write(`
|
|
@@ -34523,7 +33331,7 @@ ${green("✓")} ${bold("scheduled audits are on")}
|
|
|
34523
33331
|
`).join(""));
|
|
34524
33332
|
}
|
|
34525
33333
|
warnIfDaemonWontRun();
|
|
34526
|
-
if (
|
|
33334
|
+
if (prompted) {
|
|
34527
33335
|
outro("failproofai audit --status · when the next scan is due");
|
|
34528
33336
|
} else {
|
|
34529
33337
|
process.stdout.write(dim(`
|
|
@@ -34569,13 +33377,14 @@ function runScheduleStatus() {
|
|
|
34569
33377
|
on ? `${green("on")}${dim(` every ${config.audit.intervalDays} days`)}` : dim("off")
|
|
34570
33378
|
]);
|
|
34571
33379
|
const live = auth && auth.refresh_expires_at * 1000 > Date.now() ? auth : null;
|
|
34572
|
-
detail.push([
|
|
34573
|
-
"leak notifications",
|
|
34574
|
-
config.audit.notify ? green("on") : dim("off (failproofai audit --notify)")
|
|
34575
|
-
]);
|
|
34576
33380
|
detail.push(["reports to", live ? live.user.email : dim("— signed out")]);
|
|
34577
33381
|
if (on && !live) {
|
|
34578
|
-
detail.push(["", pink("scans
|
|
33382
|
+
detail.push(["", pink("scans continue; digests are paused until you sign in")]);
|
|
33383
|
+
} else if (on && config.audit.reportsConsentedAt === undefined) {
|
|
33384
|
+
detail.push([
|
|
33385
|
+
"",
|
|
33386
|
+
pink("scans continue; digests need a fresh opt-in — run `--schedule` to turn them on")
|
|
33387
|
+
]);
|
|
34579
33388
|
}
|
|
34580
33389
|
detail.push(["daemon", describeDaemon(daemon)]);
|
|
34581
33390
|
if (sched?.nextDueAtMs != null && on) {
|
|
@@ -34659,22 +33468,6 @@ function agoPhrase(ms) {
|
|
|
34659
33468
|
return `${m}m ago`;
|
|
34660
33469
|
return "just now";
|
|
34661
33470
|
}
|
|
34662
|
-
function runNotifyToggle(enable) {
|
|
34663
|
-
try {
|
|
34664
|
-
updateConfig({ audit: { notify: enable } });
|
|
34665
|
-
} catch (err) {
|
|
34666
|
-
process.stderr.write(`Could not update the setting: ${err instanceof Error ? err.message : String(err)}
|
|
34667
|
-
`);
|
|
34668
|
-
process.exit(1);
|
|
34669
|
-
}
|
|
34670
|
-
process.stdout.write(enable ? `
|
|
34671
|
-
${green("on")} leak notifications — this machine will tell you when a scan finds a credential.
|
|
34672
|
-
|
|
34673
|
-
` : `
|
|
34674
|
-
${dim("off")} leak notifications — scans continue, and findings still appear in ` + `\`failproofai audit\` and the emailed digest.
|
|
34675
|
-
|
|
34676
|
-
`);
|
|
34677
|
-
}
|
|
34678
33471
|
var MIN_DAYS = 1, MAX_DAYS = 90, ScheduleCliError, styled = () => colorsEnabled(process.stdout), wrap3 = (open, s) => styled() ? `${open}${s}${ANSI_RESET}` : s, pink = (s) => wrap3(brandAnsi("pink"), s), green = (s) => wrap3(brandAnsi("guide"), s), dim = (s) => wrap3(ANSI_DIM, s), bold = (s) => wrap3(ANSI_BOLD, s);
|
|
34679
33472
|
var init_schedule_cli = __esm(() => {
|
|
34680
33473
|
init_fp_config();
|
|
@@ -34690,14 +33483,14 @@ var init_schedule_cli = __esm(() => {
|
|
|
34690
33483
|
});
|
|
34691
33484
|
|
|
34692
33485
|
// scripts/parse-script-args.ts
|
|
34693
|
-
import { resolve as
|
|
33486
|
+
import { resolve as resolve30 } from "path";
|
|
34694
33487
|
function parseStringFlag(flagName, errorLabel, inlineValue, args, index, options) {
|
|
34695
33488
|
const raw = inlineValue ?? args[index + 1];
|
|
34696
33489
|
if (raw === undefined || inlineValue === null && raw.startsWith("-")) {
|
|
34697
33490
|
console.error(`Error: ${flagName} requires ${errorLabel}`);
|
|
34698
33491
|
process.exit(1);
|
|
34699
33492
|
}
|
|
34700
|
-
const value = options?.resolve ?
|
|
33493
|
+
const value = options?.resolve ? resolve30(raw) : raw;
|
|
34701
33494
|
return { value, spliceCount: inlineValue !== null ? 1 : 2 };
|
|
34702
33495
|
}
|
|
34703
33496
|
function parseScriptArgs(argv) {
|
|
@@ -34753,26 +33546,26 @@ function parseScriptArgs(argv) {
|
|
|
34753
33546
|
var init_parse_script_args = () => {};
|
|
34754
33547
|
|
|
34755
33548
|
// scripts/install-diagnosis.mjs
|
|
34756
|
-
import { existsSync as
|
|
34757
|
-
import { dirname as
|
|
34758
|
-
import { homedir as
|
|
33549
|
+
import { existsSync as existsSync43, readFileSync as readFileSync36, realpathSync as realpathSync4 } from "node:fs";
|
|
33550
|
+
import { dirname as dirname21, resolve as resolve31 } from "node:path";
|
|
33551
|
+
import { homedir as homedir31, platform as platform5 } from "node:os";
|
|
34759
33552
|
import { spawnSync } from "node:child_process";
|
|
34760
33553
|
function findPackageRoot(start) {
|
|
34761
33554
|
try {
|
|
34762
33555
|
let dir = realpathSync4(start);
|
|
34763
|
-
if (
|
|
34764
|
-
dir =
|
|
33556
|
+
if (existsSync43(dir) && !existsSync43(resolve31(dir, "package.json"))) {
|
|
33557
|
+
dir = dirname21(dir);
|
|
34765
33558
|
}
|
|
34766
33559
|
while (true) {
|
|
34767
|
-
const pkgPath =
|
|
34768
|
-
if (
|
|
33560
|
+
const pkgPath = resolve31(dir, "package.json");
|
|
33561
|
+
if (existsSync43(pkgPath)) {
|
|
34769
33562
|
try {
|
|
34770
|
-
const pkg = JSON.parse(
|
|
33563
|
+
const pkg = JSON.parse(readFileSync36(pkgPath, "utf8"));
|
|
34771
33564
|
if (pkg.name === PKG_NAME)
|
|
34772
33565
|
return dir;
|
|
34773
33566
|
} catch {}
|
|
34774
33567
|
}
|
|
34775
|
-
const parent =
|
|
33568
|
+
const parent = dirname21(dir);
|
|
34776
33569
|
if (parent === dir)
|
|
34777
33570
|
return null;
|
|
34778
33571
|
dir = parent;
|
|
@@ -34785,7 +33578,7 @@ function readPackageVersion(packageRoot) {
|
|
|
34785
33578
|
if (!packageRoot)
|
|
34786
33579
|
return null;
|
|
34787
33580
|
try {
|
|
34788
|
-
const pkg = JSON.parse(
|
|
33581
|
+
const pkg = JSON.parse(readFileSync36(resolve31(packageRoot, "package.json"), "utf8"));
|
|
34789
33582
|
return typeof pkg.version === "string" ? pkg.version : null;
|
|
34790
33583
|
} catch {
|
|
34791
33584
|
return null;
|
|
@@ -34811,16 +33604,16 @@ function locateNpmGlobal() {
|
|
|
34811
33604
|
const root = (res.stdout || "").trim();
|
|
34812
33605
|
if (!root)
|
|
34813
33606
|
return null;
|
|
34814
|
-
const candidate =
|
|
34815
|
-
return
|
|
33607
|
+
const candidate = resolve31(root, PKG_NAME);
|
|
33608
|
+
return existsSync43(resolve31(candidate, "package.json")) ? candidate : null;
|
|
34816
33609
|
} catch {
|
|
34817
33610
|
return null;
|
|
34818
33611
|
}
|
|
34819
33612
|
}
|
|
34820
33613
|
function locateBunGlobal() {
|
|
34821
33614
|
try {
|
|
34822
|
-
const candidate =
|
|
34823
|
-
return
|
|
33615
|
+
const candidate = resolve31(homedir31(), ".bun", "install", "global", "node_modules", PKG_NAME);
|
|
33616
|
+
return existsSync43(resolve31(candidate, "package.json")) ? candidate : null;
|
|
34824
33617
|
} catch {
|
|
34825
33618
|
return null;
|
|
34826
33619
|
}
|
|
@@ -34828,8 +33621,8 @@ function locateBunGlobal() {
|
|
|
34828
33621
|
function buildRecommendation(pathFirstBin) {
|
|
34829
33622
|
if (!pathFirstBin)
|
|
34830
33623
|
return null;
|
|
34831
|
-
const bunBinPrefix =
|
|
34832
|
-
const bunGlobalPrefix =
|
|
33624
|
+
const bunBinPrefix = resolve31(homedir31(), ".bun", "bin") + "/";
|
|
33625
|
+
const bunGlobalPrefix = resolve31(homedir31(), ".bun", "install", "global") + "/";
|
|
34833
33626
|
const isBun = pathFirstBin.startsWith(bunBinPrefix) || pathFirstBin.startsWith(bunGlobalPrefix);
|
|
34834
33627
|
if (isBun) {
|
|
34835
33628
|
return `rm -f ~/.bun/bin/${PKG_NAME} && rm -rf ~/.bun/install/global/node_modules/${PKG_NAME}`;
|
|
@@ -34923,8 +33716,8 @@ var init_dashboard_host = __esm(() => {
|
|
|
34923
33716
|
|
|
34924
33717
|
// scripts/launch.ts
|
|
34925
33718
|
import { spawn as spawn2 } from "child_process";
|
|
34926
|
-
import { realpathSync as realpathSync5, existsSync as
|
|
34927
|
-
import { resolve as
|
|
33719
|
+
import { realpathSync as realpathSync5, existsSync as existsSync44 } from "node:fs";
|
|
33720
|
+
import { resolve as resolve32, dirname as dirname22 } from "node:path";
|
|
34928
33721
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
34929
33722
|
import { createInterface } from "node:readline";
|
|
34930
33723
|
function launch(mode) {
|
|
@@ -34948,9 +33741,9 @@ function launch(mode) {
|
|
|
34948
33741
|
process.env.PORT = port;
|
|
34949
33742
|
process.env.HOSTNAME = bindHost;
|
|
34950
33743
|
cmd = "node";
|
|
34951
|
-
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ??
|
|
34952
|
-
const serverJsPath =
|
|
34953
|
-
if (!
|
|
33744
|
+
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve32(dirname22(realpathSync5(fileURLToPath2(import.meta.url))), "..");
|
|
33745
|
+
const serverJsPath = resolve32(packageRoot, ".next/standalone/server.js");
|
|
33746
|
+
if (!existsSync44(serverJsPath)) {
|
|
34954
33747
|
let shadowMessage = null;
|
|
34955
33748
|
try {
|
|
34956
33749
|
const diag = diagnoseShadow({ selfPackageRoot: packageRoot, selfVersion: version2 });
|
|
@@ -35033,27 +33826,21 @@ function helpText() {
|
|
|
35033
33826
|
const lines = helpScreen({
|
|
35034
33827
|
command: "audit",
|
|
35035
33828
|
version: version2,
|
|
35036
|
-
tagline: "
|
|
33829
|
+
tagline: "review your agent CLIs for risky and wasteful patterns",
|
|
35037
33830
|
sections: [
|
|
35038
33831
|
{
|
|
35039
33832
|
label: "usage",
|
|
35040
33833
|
entries: [
|
|
35041
|
-
["(bare)", `Scan your session history
|
|
35042
|
-
["--schedule [days]", "Scan on a timer and
|
|
35043
|
-
["--email <address>", "With --schedule,
|
|
33834
|
+
["(bare)", `Scan your session history, then open http://localhost:${DASHBOARD_PORT}/audit`],
|
|
33835
|
+
["--schedule [days]", "Scan on a timer and email the findings. Default 7 days, range 1-90. Signs you in the first time."],
|
|
33836
|
+
["--email <address>", "With --schedule, skips the sign-in prompt."],
|
|
35044
33837
|
["--no-schedule", "Stop the timer. Leaves you signed in."],
|
|
35045
|
-
["--notify", "Show a desktop notification when a scan finds a credential. On by default."],
|
|
35046
|
-
["--no-notify", "Stop desktop notifications. Scheduled scans and optional email alerts are unaffected."],
|
|
35047
33838
|
["--status", "Whether scheduling is on, where reports go, the daemon's state, and when the next scan is due."],
|
|
35048
33839
|
["-h, --help", "Show this help."]
|
|
35049
33840
|
]
|
|
35050
33841
|
}
|
|
35051
33842
|
],
|
|
35052
|
-
footer: [
|
|
35053
|
-
"Every scan runs on this machine. Leak alerts appear as desktop system",
|
|
35054
|
-
"notifications, never inside agent sessions. If you add email, only the",
|
|
35055
|
-
"newest masked credential exposure and its metadata leave the machine."
|
|
35056
|
-
]
|
|
33843
|
+
footer: ["Everything runs on this machine; only a scheduled digest ever leaves it."]
|
|
35057
33844
|
}, helpOptsFor(process.stdout));
|
|
35058
33845
|
return ["", ...lines, ""].join(`
|
|
35059
33846
|
`) + `
|
|
@@ -35202,40 +33989,6 @@ function heldByLine(held) {
|
|
|
35202
33989
|
const ageS = Math.max(0, Math.round((Date.now() - held.startedAt) / 1000));
|
|
35203
33990
|
return `another audit is already running (pid ${held.pid}, started by ${held.source} ${ageS}s ago)`;
|
|
35204
33991
|
}
|
|
35205
|
-
async function announceLeaksOnDesktop(result, report) {
|
|
35206
|
-
try {
|
|
35207
|
-
await announceLeaksOrThrow(result, report);
|
|
35208
|
-
} catch (err) {
|
|
35209
|
-
process.stderr.write(`failproofai: could not announce a leak on the desktop: ` + `${err instanceof Error ? err.message : String(err)}
|
|
35210
|
-
`);
|
|
35211
|
-
}
|
|
35212
|
-
}
|
|
35213
|
-
async function announceLeaksOrThrow(result, report) {
|
|
35214
|
-
const ids = result.leakIds ?? result.newLeakIds ?? [];
|
|
35215
|
-
if (ids.length === 0)
|
|
35216
|
-
return;
|
|
35217
|
-
if (!readConfig().audit.notify)
|
|
35218
|
-
return;
|
|
35219
|
-
const n = ids.length === 1 ? "a possible credential exposure" : `${ids.length} possible credential exposures`;
|
|
35220
|
-
const emailState = report.kind === "sent" ? "sent" : report.kind === "signed-out" || !readAuth() ? "missing" : report.kind === "failed" ? "failed" : "held";
|
|
35221
|
-
const { title, body } = desktopLeakNotice(emailState);
|
|
35222
|
-
if (process.platform === "darwin") {
|
|
35223
|
-
pruneMacNotifyQueue();
|
|
35224
|
-
if (!queueMacNotification(ids[0], title, body)) {
|
|
35225
|
-
process.stderr.write(`failproofai: found ${n} but could not queue a notification
|
|
35226
|
-
`);
|
|
35227
|
-
} else if (!macNotifierInstalled()) {
|
|
35228
|
-
process.stderr.write(`failproofai: found ${n}; the notifier is not installed, so nothing will show it ` + `(run \`failproofai config\`)
|
|
35229
|
-
`);
|
|
35230
|
-
}
|
|
35231
|
-
return;
|
|
35232
|
-
}
|
|
35233
|
-
const outcome = await notifyDesktop(title, body, 0);
|
|
35234
|
-
if (!outcome.ok) {
|
|
35235
|
-
process.stderr.write(`failproofai: found ${n} but could not raise a desktop notification (${outcome.reason})
|
|
35236
|
-
`);
|
|
35237
|
-
}
|
|
35238
|
-
}
|
|
35239
33992
|
async function runScheduledAudit() {
|
|
35240
33993
|
const attempt = acquireAuditLock("scheduled");
|
|
35241
33994
|
if (!attempt.ok) {
|
|
@@ -35268,7 +34021,6 @@ async function runScheduledAudit() {
|
|
|
35268
34021
|
process.stdout.write(`failproofai: audit complete — ${num2(result.eventsScanned)} tool calls across ` + `${num2(result.transcripts.scanned)} sessions, ${num2(result.totals.hits)} hits
|
|
35269
34022
|
`);
|
|
35270
34023
|
const outcome = await reportHarm(result);
|
|
35271
|
-
await announceLeaksOnDesktop(result, outcome);
|
|
35272
34024
|
const line = describeOutcome2(outcome);
|
|
35273
34025
|
if (line) {
|
|
35274
34026
|
const stream = outcome.kind === "sent" ? process.stdout : process.stderr;
|
|
@@ -35299,16 +34051,6 @@ async function runAuditCli(args) {
|
|
|
35299
34051
|
runScheduleStatus();
|
|
35300
34052
|
process.exit(0);
|
|
35301
34053
|
}
|
|
35302
|
-
for (const [flag, enable] of [["--notify", true], ["--no-notify", false]]) {
|
|
35303
|
-
if (args.includes(flag)) {
|
|
35304
|
-
const extra = args.find((a) => a !== flag);
|
|
35305
|
-
if (extra)
|
|
35306
|
-
die(`\`audit ${flag}\` takes no other arguments (got: ${extra}).`);
|
|
35307
|
-
await Promise.resolve().then(() => init_schedule_cli());
|
|
35308
|
-
runNotifyToggle(enable);
|
|
35309
|
-
process.exit(0);
|
|
35310
|
-
}
|
|
35311
|
-
}
|
|
35312
34054
|
if (args.includes("--no-schedule")) {
|
|
35313
34055
|
const extra = args.find((a) => a !== "--no-schedule");
|
|
35314
34056
|
if (extra)
|
|
@@ -35424,10 +34166,6 @@ var init_cli2 = __esm(() => {
|
|
|
35424
34166
|
init_telemetry_sanitize();
|
|
35425
34167
|
init_open_browser();
|
|
35426
34168
|
init_report_harm();
|
|
35427
|
-
init_desktop_notify();
|
|
35428
|
-
init_macos_notifier();
|
|
35429
|
-
init_fp_config();
|
|
35430
|
-
init_auth_store();
|
|
35431
34169
|
init_tui();
|
|
35432
34170
|
init_package();
|
|
35433
34171
|
AUDIT_STAGES2 = [
|
|
@@ -35760,8 +34498,8 @@ var init_session_pause_cli = __esm(() => {
|
|
|
35760
34498
|
|
|
35761
34499
|
// scripts/launch.ts
|
|
35762
34500
|
import { spawn as spawn3 } from "child_process";
|
|
35763
|
-
import { realpathSync as realpathSync6, existsSync as
|
|
35764
|
-
import { resolve as
|
|
34501
|
+
import { realpathSync as realpathSync6, existsSync as existsSync45 } from "node:fs";
|
|
34502
|
+
import { resolve as resolve33, dirname as dirname23 } from "node:path";
|
|
35765
34503
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
35766
34504
|
import { createInterface as createInterface2 } from "node:readline";
|
|
35767
34505
|
function launch2(mode) {
|
|
@@ -35785,9 +34523,9 @@ function launch2(mode) {
|
|
|
35785
34523
|
process.env.PORT = port;
|
|
35786
34524
|
process.env.HOSTNAME = bindHost;
|
|
35787
34525
|
cmd = "node";
|
|
35788
|
-
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ??
|
|
35789
|
-
const serverJsPath =
|
|
35790
|
-
if (!
|
|
34526
|
+
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve33(dirname23(realpathSync6(fileURLToPath3(import.meta.url))), "..");
|
|
34527
|
+
const serverJsPath = resolve33(packageRoot, ".next/standalone/server.js");
|
|
34528
|
+
if (!existsSync45(serverJsPath)) {
|
|
35791
34529
|
let shadowMessage = null;
|
|
35792
34530
|
try {
|
|
35793
34531
|
const diag = diagnoseShadow({ selfPackageRoot: packageRoot, selfVersion: version2 });
|
|
@@ -35884,17 +34622,17 @@ var init_cli_error2 = __esm(() => {
|
|
|
35884
34622
|
|
|
35885
34623
|
// bin/failproofai.mjs
|
|
35886
34624
|
import { realpathSync as realpathSync7 } from "fs";
|
|
35887
|
-
import { dirname as
|
|
34625
|
+
import { dirname as dirname24, resolve as resolve34 } from "path";
|
|
35888
34626
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
35889
34627
|
// package.json
|
|
35890
|
-
var version = "1.0.4
|
|
34628
|
+
var version = "1.0.4";
|
|
35891
34629
|
|
|
35892
34630
|
// bin/failproofai.mjs
|
|
35893
34631
|
if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
|
|
35894
|
-
process.env.FAILPROOFAI_PACKAGE_ROOT =
|
|
34632
|
+
process.env.FAILPROOFAI_PACKAGE_ROOT = resolve34(dirname24(realpathSync7(fileURLToPath4(import.meta.url))), "..");
|
|
35895
34633
|
}
|
|
35896
34634
|
if (!process.env.FAILPROOFAI_DIST_PATH) {
|
|
35897
|
-
process.env.FAILPROOFAI_DIST_PATH =
|
|
34635
|
+
process.env.FAILPROOFAI_DIST_PATH = resolve34(dirname24(realpathSync7(fileURLToPath4(import.meta.url))), "..", "dist");
|
|
35898
34636
|
}
|
|
35899
34637
|
var args = process.argv.slice(2);
|
|
35900
34638
|
if (args[0] === "p" || args[0] === "policy")
|
|
@@ -36132,7 +34870,7 @@ Run \`failproofai help\` for usage.`);
|
|
|
36132
34870
|
label: "see what it caught",
|
|
36133
34871
|
entries: [
|
|
36134
34872
|
["(no args)", "Open the policy dashboard on localhost:8020"],
|
|
36135
|
-
["audit", "
|
|
34873
|
+
["audit", "Scan your agents' history, then open the audit view"],
|
|
36136
34874
|
["config --status", "Cloud connection, daemon version, pause state"]
|
|
36137
34875
|
]
|
|
36138
34876
|
},
|