failproofai 1.0.4-beta.8 → 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 +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 +4 -4
- 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 +5 -5
- 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]__0hmcwgg._.js → [externals]__0-9ti7z._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__14b7cc6._.js → [externals]__0u3wrvq._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-219ec._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__04_h-a5._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__08dchfu._.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]__1le9jqc._.js +3 -0
- 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 +5 -0
- 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/{_1_eakl4._.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]__187jks6._.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]__1fa1jd9._.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]__1kpd1za._.js → [root-of-the-server]__190won8._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ctpynv._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1nq7ivq._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__00y9ww2._.js → [root-of-the-server]__1sc84bb._.js} +2 -21
- package/.next/standalone/.next/server/chunks/ssr/_02yoeej._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_00bqkhj._.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/{_14c6q09._.js → _1if9ixi._.js} +2 -21
- 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/lib_openclaw-projects_ts_0qxq3bp._.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/{1302duobnz07l.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/{2n85g-crgcx5j.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/{1chm9piluggc-.js → 2lwipmv5etc8m.js} +1 -1
- package/.next/standalone/.next/static/chunks/2pd16tn5qmx13.js +1 -0
- package/.next/standalone/.next/static/chunks/{2ukucf6g-kbs5.js → 2u8vh73tpw28_.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1_x7pqemw5n_w.js → 31u94yukw9fz-.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2mizt96igig1y.js → 3p9_8x5ezce-9.js} +1 -1
- package/.next/standalone/.next/static/chunks/3qfoeqhy5l10x.js +1 -0
- package/.next/standalone/.next/static/chunks/{0vm4wdx3w-2_w.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/download-session.ts +13 -58
- package/.next/standalone/lib/openclaw-projects.ts +18 -54
- package/.next/standalone/lib/openclaw-sessions.ts +17 -75
- 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 +664 -2121
- package/dist/worker.mjs +105 -422
- 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/download-session.ts +13 -58
- package/lib/openclaw-projects.ts +18 -54
- package/lib/openclaw-sessions.ts +17 -75
- 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/[root-of-the-server]__0faqz9x._.js +0 -22
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zyc_nm._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__112b4go._.js +0 -24
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1l71g3y._.js +0 -22
- package/.next/standalone/.next/server/chunks/lib_openclaw-projects_ts_0gc-p24._.js +0 -3
- 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]__0n2txhk._.js +0 -3
- 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/[root-of-the-server]__1kaotix._.js +0 -22
- 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/0igq884ziuv-8.js +0 -1
- package/.next/standalone/.next/static/chunks/349jkb79v5g6z.js +0 -1
- package/.next/standalone/.next/static/chunks/3a4gth64hwtwf.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/.next/standalone/lib/openclaw-db.ts +0 -271
- package/lib/openclaw-db.ts +0 -271
- 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/{DNhaAH8iPGH0tIuxee5_2 → dg8VQ7Ejlo0Qsh_x92oIt}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{DNhaAH8iPGH0tIuxee5_2 → dg8VQ7Ejlo0Qsh_x92oIt}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{DNhaAH8iPGH0tIuxee5_2 → dg8VQ7Ejlo0Qsh_x92oIt}/_ssgManifest.js +0 -0
package/src/audit/cli.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* the dashboard to view it.
|
|
4
4
|
*
|
|
5
5
|
* failproofai audit Scan, then launch the dashboard at /audit.
|
|
6
|
-
* failproofai audit --schedule Put
|
|
6
|
+
* failproofai audit --schedule Put scans on a timer and mail the findings.
|
|
7
7
|
* failproofai audit --no-schedule Stop the timer.
|
|
8
8
|
* failproofai audit --status What this machine is scheduled to do.
|
|
9
9
|
* failproofai audit -h, --help Show usage.
|
|
@@ -35,11 +35,6 @@ import { getInstanceId } from "../../lib/telemetry-id";
|
|
|
35
35
|
import { sanitizeErrorMessage } from "../../lib/telemetry-sanitize";
|
|
36
36
|
import { openWhenReady } from "./open-browser";
|
|
37
37
|
import { describeOutcome, reportHarm } from "./report-harm";
|
|
38
|
-
import { notifyDesktop } from "./desktop-notify";
|
|
39
|
-
import { macNotifierInstalled, pruneMacNotifyQueue, queueMacNotification } from "./macos-notifier";
|
|
40
|
-
import { desktopLeakNotice, type DesktopLeakEmailState } from "../hooks/notice";
|
|
41
|
-
import { readConfig } from "../hooks/fp-config";
|
|
42
|
-
import { readAuth } from "../../lib/auth/auth-store";
|
|
43
38
|
import { brandAnsi, ANSI_RESET, ANSI_BOLD, ANSI_DIM, helpScreen, helpOptsFor } from "../hooks/tui";
|
|
44
39
|
import { version } from "../../package.json";
|
|
45
40
|
|
|
@@ -97,36 +92,26 @@ export function helpText(): string {
|
|
|
97
92
|
{
|
|
98
93
|
command: "audit",
|
|
99
94
|
version,
|
|
100
|
-
tagline: "
|
|
95
|
+
tagline: "review your agent CLIs for risky and wasteful patterns",
|
|
101
96
|
sections: [
|
|
102
97
|
{
|
|
103
98
|
label: "usage",
|
|
104
99
|
entries: [
|
|
105
|
-
["(bare)", `Scan your session history
|
|
106
|
-
["--schedule [days]", "Scan on a timer and
|
|
100
|
+
["(bare)", `Scan your session history, then open http://localhost:${DASHBOARD_PORT}/audit`],
|
|
101
|
+
["--schedule [days]", "Scan on a timer and email the findings. Default 7 days, range 1-90. Signs you in the first time."],
|
|
107
102
|
// Its own row now, rather than a clause inside --schedule's. It
|
|
108
103
|
// does not stand alone, which is why it used to be a clause — but
|
|
109
104
|
// a clause wraps, and `--email <address>` landing with the flag at
|
|
110
105
|
// the end of one line and its placeholder at the start of the next
|
|
111
106
|
// is not a flag anybody can read or copy.
|
|
112
|
-
["--email <address>", "With --schedule,
|
|
107
|
+
["--email <address>", "With --schedule, skips the sign-in prompt."],
|
|
113
108
|
["--no-schedule", "Stop the timer. Leaves you signed in."],
|
|
114
|
-
// Its own pair of flags rather than a clause on --schedule: whether
|
|
115
|
-
// this machine scans and whether it may interrupt you are separate
|
|
116
|
-
// decisions, and nobody silencing a banner should have to re-state
|
|
117
|
-
// their schedule to do it.
|
|
118
|
-
["--notify", "Show a desktop notification when a scan finds a credential. On by default."],
|
|
119
|
-
["--no-notify", "Stop desktop notifications. Scheduled scans and optional email alerts are unaffected."],
|
|
120
109
|
["--status", "Whether scheduling is on, where reports go, the daemon's state, and when the next scan is due."],
|
|
121
110
|
["-h, --help", "Show this help."],
|
|
122
111
|
],
|
|
123
112
|
},
|
|
124
113
|
],
|
|
125
|
-
footer: [
|
|
126
|
-
"Every scan runs on this machine. Leak alerts appear as desktop system",
|
|
127
|
-
"notifications, never inside agent sessions. If you add email, only the",
|
|
128
|
-
"newest masked credential exposure and its metadata leave the machine.",
|
|
129
|
-
],
|
|
114
|
+
footer: ["Everything runs on this machine; only a scheduled digest ever leaves it."],
|
|
130
115
|
},
|
|
131
116
|
helpOptsFor(process.stdout),
|
|
132
117
|
);
|
|
@@ -214,18 +199,6 @@ function startProgress(): Progress {
|
|
|
214
199
|
const lines = Array.from({ length: n }, (_, i) => lineFor(i));
|
|
215
200
|
// Move the cursor back up over the previously-drawn block, then clear and
|
|
216
201
|
// rewrite each line in place.
|
|
217
|
-
//
|
|
218
|
-
// INVARIANT: nothing else may write to the terminal while this is running.
|
|
219
|
-
// The cursor-up is a fixed count, so any stray line pushes the cursor down
|
|
220
|
-
// and the next redraw repaints the block lower — stranding the top of the
|
|
221
|
-
// old frame above it, which reads as the audit having run twice. That is
|
|
222
|
-
// not hypothetical: Node's "SQLite is an experimental feature" warning did
|
|
223
|
-
// exactly this on every first run, two lines of it, and it is why
|
|
224
|
-
// `lib/sqlite-reader.ts` now filters that one warning at the source.
|
|
225
|
-
//
|
|
226
|
-
// There is no defensive fix from inside here — erasing to end of screen
|
|
227
|
-
// still leaves the stranded lines ABOVE the cursor. Keep the terminal
|
|
228
|
-
// quiet instead.
|
|
229
202
|
if (printed) process.stdout.write(`\x1b[${n}A`);
|
|
230
203
|
process.stdout.write(lines.map((l) => `\x1b[2K${l}`).join("\n") + "\n");
|
|
231
204
|
printed = true;
|
|
@@ -361,116 +334,6 @@ function heldByLine(held: AuditLockInfo | null): string {
|
|
|
361
334
|
return `another audit is already running (pid ${held.pid}, started by ${held.source} ${ageS}s ago)`;
|
|
362
335
|
}
|
|
363
336
|
|
|
364
|
-
/**
|
|
365
|
-
* Raise a desktop banner for credentials this scan found and nothing has
|
|
366
|
-
* announced yet.
|
|
367
|
-
*
|
|
368
|
-
* Scheduled runs only, and the reason is the same one that keeps `reportHarm`
|
|
369
|
-
* here: an interactive `failproofai audit` prints its findings to a person who
|
|
370
|
-
* is already looking at them, so a banner on top is noise. The scheduled run is
|
|
371
|
-
* the one with nobody watching, which is exactly when a notification is the
|
|
372
|
-
* only thing that reaches anyone.
|
|
373
|
-
*
|
|
374
|
-
* Every failure is swallowed on purpose. This is an announcement about a
|
|
375
|
-
* completed scan, not part of the scan: a machine with no desktop, a locked
|
|
376
|
-
* session, a bus that hung — none of those make the audit itself a failure, and
|
|
377
|
-
* turning a good scan into exit 1 would make the scheduler back off from
|
|
378
|
-
* running the thing that actually matters.
|
|
379
|
-
*/
|
|
380
|
-
async function announceLeaksOnDesktop(
|
|
381
|
-
result: AuditResult,
|
|
382
|
-
report: Awaited<ReturnType<typeof reportHarm>>,
|
|
383
|
-
): Promise<void> {
|
|
384
|
-
try {
|
|
385
|
-
await announceLeaksOrThrow(result, report);
|
|
386
|
-
} catch (err) {
|
|
387
|
-
// The catch is not decoration. `notifyDesktop` documents that it never
|
|
388
|
-
// throws, but it is doing socket I/O, and so are the config read and the
|
|
389
|
-
// marker write beside it — depending on three modules' promises to keep a
|
|
390
|
-
// completed scan from reporting failure is a dependency this does not need
|
|
391
|
-
// to have.
|
|
392
|
-
process.stderr.write(
|
|
393
|
-
`failproofai: could not announce a leak on the desktop: ` +
|
|
394
|
-
`${err instanceof Error ? err.message : String(err)}\n`,
|
|
395
|
-
);
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
async function announceLeaksOrThrow(
|
|
400
|
-
result: AuditResult,
|
|
401
|
-
report: Awaited<ReturnType<typeof reportHarm>>,
|
|
402
|
-
): Promise<void> {
|
|
403
|
-
// `leakIds` includes credentials this scan encountered before. A scheduled
|
|
404
|
-
// run is a fresh safety check, so it must raise a fresh system notification
|
|
405
|
-
// when the leak is still present; the old per-finding marker is why only the
|
|
406
|
-
// first test run ever produced a banner. Older cached results fall back to
|
|
407
|
-
// first-seen ids.
|
|
408
|
-
const ids = result.leakIds ?? result.newLeakIds ?? [];
|
|
409
|
-
if (ids.length === 0) return;
|
|
410
|
-
// Read at fire time, not at scan time: the switch answers "should this
|
|
411
|
-
// interrupt me", and it is the user's most recent answer that counts.
|
|
412
|
-
if (!readConfig().audit.notify) return;
|
|
413
|
-
|
|
414
|
-
const n = ids.length === 1 ? "a possible credential exposure" : `${ids.length} possible credential exposures`;
|
|
415
|
-
// Fixed text with no scanned content and no count. A finding's own text comes
|
|
416
|
-
// from a repository this machine cloned, and notification bodies render
|
|
417
|
-
// markup on several Linux desktops. The count is intentionally left to the
|
|
418
|
-
// report too: a banner must not turn detector candidates into a claim that
|
|
419
|
-
// the user leaked N real credentials.
|
|
420
|
-
//
|
|
421
|
-
// No action buttons, deliberately. `Notify` supports them, but a server
|
|
422
|
-
// delivers the click back as an `ActionInvoked` signal to the sender — and
|
|
423
|
-
// this process exits as soon as the scan finishes, so the button would be
|
|
424
|
-
// dead. A plain command the user can copy beats a button that does nothing.
|
|
425
|
-
const emailState: DesktopLeakEmailState =
|
|
426
|
-
report.kind === "sent"
|
|
427
|
-
? "sent"
|
|
428
|
-
: report.kind === "signed-out" || !readAuth()
|
|
429
|
-
? "missing"
|
|
430
|
-
: report.kind === "failed"
|
|
431
|
-
? "failed"
|
|
432
|
-
: "held";
|
|
433
|
-
const { title, body } = desktopLeakNotice(emailState);
|
|
434
|
-
|
|
435
|
-
// The two platforms need opposite things, and the split is not cosmetic. On
|
|
436
|
-
// Linux this process can reach the session bus itself. On macOS it cannot
|
|
437
|
-
// reach Notification Center at all — it is a child of a LaunchDaemon, outside
|
|
438
|
-
// the GUI session — so it hands the message to an agent that lives inside
|
|
439
|
-
// one. See `macos-notifier.ts` for why that agent exists.
|
|
440
|
-
if (process.platform === "darwin") {
|
|
441
|
-
pruneMacNotifyQueue();
|
|
442
|
-
if (!queueMacNotification(ids[0], title, body)) {
|
|
443
|
-
process.stderr.write(`failproofai: found ${n} but could not queue a notification\n`);
|
|
444
|
-
} else if (!macNotifierInstalled()) {
|
|
445
|
-
// Queued, but nothing currently collects it. The payload is durable and
|
|
446
|
-
// will be consumed when the LaunchAgent returns.
|
|
447
|
-
// Worth a line, because the immediate symptom is otherwise silence.
|
|
448
|
-
process.stderr.write(
|
|
449
|
-
`failproofai: found ${n}; the notifier is not installed, so nothing will show it ` +
|
|
450
|
-
`(run \`failproofai config\`)\n`,
|
|
451
|
-
);
|
|
452
|
-
}
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
const outcome = await notifyDesktop(
|
|
457
|
-
title,
|
|
458
|
-
body,
|
|
459
|
-
// `0` asks the notification server for a fresh id. Each scheduled scan that
|
|
460
|
-
// still sees a leak is a new reminder, rather than replacing or suppressing
|
|
461
|
-
// the first banner forever.
|
|
462
|
-
0,
|
|
463
|
-
);
|
|
464
|
-
if (!outcome.ok) {
|
|
465
|
-
// stderr, so it lands in the journal beside the run it belongs to. Not a
|
|
466
|
-
// failure of the audit — but "we found a key and could not tell you" is
|
|
467
|
-
// exactly the sentence someone debugging this needs to find.
|
|
468
|
-
process.stderr.write(
|
|
469
|
-
`failproofai: found ${n} but could not raise a desktop notification (${outcome.reason})\n`,
|
|
470
|
-
);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
337
|
// ── Headless (scheduled) audit ───────────────────────────────────────────────
|
|
475
338
|
|
|
476
339
|
/**
|
|
@@ -541,9 +404,7 @@ export async function runScheduledAudit(): Promise<number> {
|
|
|
541
404
|
`${num(result.transcripts.scanned)} sessions, ${num(result.totals.hits)} hits\n`,
|
|
542
405
|
);
|
|
543
406
|
|
|
544
|
-
//
|
|
545
|
-
// the desktop notification can truthfully say whether this particular
|
|
546
|
-
// alert was also emailed; every failure remains a non-fatal outcome.
|
|
407
|
+
// Report harmful findings upstream, if the user switched emailed reports on.
|
|
547
408
|
//
|
|
548
409
|
// AFTER the dashboard cache is written and AFTER the success line, because
|
|
549
410
|
// the scan is the product and this is an optional extra on top of it.
|
|
@@ -557,7 +418,6 @@ export async function runScheduledAudit(): Promise<number> {
|
|
|
557
418
|
// would also make the manual command do a network call that
|
|
558
419
|
// `audit --help` promises it does not.
|
|
559
420
|
const outcome = await reportHarm(result);
|
|
560
|
-
await announceLeaksOnDesktop(result, outcome);
|
|
561
421
|
const line = describeOutcome(outcome);
|
|
562
422
|
if (line) {
|
|
563
423
|
// Anything other than a successful send goes to stderr: on a scheduled run
|
|
@@ -692,16 +552,6 @@ export async function runAuditCli(args: string[]): Promise<void> {
|
|
|
692
552
|
process.exit(0);
|
|
693
553
|
}
|
|
694
554
|
|
|
695
|
-
for (const [flag, enable] of [["--notify", true], ["--no-notify", false]] as const) {
|
|
696
|
-
if (args.includes(flag)) {
|
|
697
|
-
const extra = args.find((a) => a !== flag);
|
|
698
|
-
if (extra) die(`\`audit ${flag}\` takes no other arguments (got: ${extra}).`);
|
|
699
|
-
const { runNotifyToggle } = await import("./schedule-cli");
|
|
700
|
-
runNotifyToggle(enable);
|
|
701
|
-
process.exit(0);
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
|
|
705
555
|
if (args.includes("--no-schedule")) {
|
|
706
556
|
const extra = args.find((a) => a !== "--no-schedule");
|
|
707
557
|
if (extra) die(`\`audit --no-schedule\` takes no other arguments (got: ${extra}).`);
|
package/src/audit/harm-report.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Turning an audit result into a harm report the api-server can act on.
|
|
3
3
|
*
|
|
4
|
-
* Runs only after a SCHEDULED scan (`failproofai audit --scheduled`)
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* simply reports `signed-out` and sends nothing.
|
|
4
|
+
* Runs only after a SCHEDULED scan (`failproofai audit --scheduled`), only when
|
|
5
|
+
* the user has switched emailed reports on, and only ever from the audit child —
|
|
6
|
+
* never the daemon, which holds no human credential precisely so that refresh
|
|
7
|
+
* rotation stays inside the audit lock. See `crates/failproofaid/src/audit_lane.rs`.
|
|
9
8
|
*
|
|
10
9
|
* ## What counts as harm
|
|
11
10
|
*
|
|
@@ -49,7 +48,6 @@
|
|
|
49
48
|
*/
|
|
50
49
|
import type { AuditCount, AuditResult } from "./types";
|
|
51
50
|
import { redactExample } from "./redact-example";
|
|
52
|
-
import type { LeakFinding } from "./leak-record";
|
|
53
51
|
|
|
54
52
|
/** Severities that mean "the engine would have stopped this". */
|
|
55
53
|
const HARMFUL_SEVERITIES = new Set(["deny", "sanitize"]);
|
|
@@ -74,58 +72,10 @@ export interface ReportedPolicy {
|
|
|
74
72
|
examples: string[];
|
|
75
73
|
}
|
|
76
74
|
|
|
77
|
-
/**
|
|
78
|
-
* One leaked credential, as the digest carries it.
|
|
79
|
-
*
|
|
80
|
-
* **Nothing here can contain the secret**, and that is a structural property
|
|
81
|
-
* rather than a promise this function keeps. `LeakFinding` has no field holding
|
|
82
|
-
* the raw value — `recordLeaks` fingerprints at the moment of detection and
|
|
83
|
-
* stores only the mask — so there is no raw value on disk for this to leak even
|
|
84
|
-
* by accident. The most a bug here can send is a mask, a length and a label.
|
|
85
|
-
*
|
|
86
|
-
* The identifier NAME (`COMPOSIO_API_KEY`) is sent, and is not a secret: it is
|
|
87
|
-
* the left-hand side of an assignment, and for the first-party class — 230 of
|
|
88
|
-
* the 237 secret-named assignments the census measured, which no vendor console
|
|
89
|
-
* can revoke — it is the only actionable thing in the finding. Without it the
|
|
90
|
-
* digest can say a key leaked and not which one.
|
|
91
|
-
*/
|
|
92
|
-
export interface ReportedLeak {
|
|
93
|
-
/** The machine-local salted id, so a reader can act on the same row twice.
|
|
94
|
-
* An HMAC under a salt that never leaves the machine — it names the finding
|
|
95
|
-
* without describing it. */
|
|
96
|
-
id: string;
|
|
97
|
-
/** WHAT. `ghp_••••••••4f2a`, or `[13-char password]` when nothing was minted. */
|
|
98
|
-
display: string;
|
|
99
|
-
label: string;
|
|
100
|
-
length: number;
|
|
101
|
-
/** False means "we know it leaked, not who issued it" — which changes the
|
|
102
|
-
* advice from "revoke at this console" to "find where this came from". */
|
|
103
|
-
attributed: boolean;
|
|
104
|
-
/** The identifier it was assigned to, when there was one. */
|
|
105
|
-
name: string | null;
|
|
106
|
-
confidence: string;
|
|
107
|
-
first_seen: string;
|
|
108
|
-
last_seen: string;
|
|
109
|
-
occurrences: number;
|
|
110
|
-
/** WHO — the harness whose transcript carried it. */
|
|
111
|
-
cli: string;
|
|
112
|
-
/** WHERE — home-shortened already, like every path in `examples`. */
|
|
113
|
-
project: string;
|
|
114
|
-
/** HOW — "read from ~/…/.env". */
|
|
115
|
-
mechanism: string;
|
|
116
|
-
/** Input means the agent SENT it (deniable at PreToolUse next time); result
|
|
117
|
-
* means the agent RECEIVED it, which no gate can undo. */
|
|
118
|
-
direction: "input" | "result";
|
|
119
|
-
}
|
|
120
|
-
|
|
121
75
|
export interface HarmReport {
|
|
122
76
|
window_from?: string;
|
|
123
77
|
window_to: string;
|
|
124
78
|
harmful: ReportedPolicy[];
|
|
125
|
-
/** Credentials seen in this window. Separate from `harmful` because it is a
|
|
126
|
-
* different kind of claim: `harmful` counts policy activity, this names a
|
|
127
|
-
* specific object that needs rotating. */
|
|
128
|
-
leaks: ReportedLeak[];
|
|
129
79
|
}
|
|
130
80
|
|
|
131
81
|
/** `failproofai/block-rm-rf` → `block-rm-rf`. */
|
|
@@ -276,64 +226,10 @@ export function selectHarmful(
|
|
|
276
226
|
* lower bound", so a policy carrying no usable timestamps is reported once on a
|
|
277
227
|
* new machine rather than silently dropped by the bound this now always sets.
|
|
278
228
|
*/
|
|
279
|
-
/**
|
|
280
|
-
* The leaks worth putting in this window's digest.
|
|
281
|
-
*
|
|
282
|
-
* Windowed on `lastSeen`, not `firstSeen`: a key first seen months ago and used
|
|
283
|
-
* again yesterday is live, and reporting it only in the window it debuted would
|
|
284
|
-
* mean the digest goes quiet on exactly the credentials still in circulation.
|
|
285
|
-
*
|
|
286
|
-
* Dismissed findings never appear. A person who looked at a row and said "that
|
|
287
|
-
* is not a secret" has given the only judgement available that beats ours, and
|
|
288
|
-
* mailing it to them weekly afterwards is how a tool teaches people to filter
|
|
289
|
-
* it out of their inbox.
|
|
290
|
-
*/
|
|
291
|
-
export function selectLeaks(findings: LeakFinding[], from: Date, to: Date): ReportedLeak[] {
|
|
292
|
-
const lo = from.getTime();
|
|
293
|
-
const hi = to.getTime();
|
|
294
|
-
const out: ReportedLeak[] = [];
|
|
295
|
-
for (const f of findings) {
|
|
296
|
-
if (f.dismissedAt) continue;
|
|
297
|
-
const last = ts(f.lastSeen);
|
|
298
|
-
// No usable timestamp means it cannot be placed in a window. Included
|
|
299
|
-
// rather than dropped, for the same reason `includeUnplaceable` exists
|
|
300
|
-
// above: silence about a credential is the failure that costs something.
|
|
301
|
-
if (last !== null && (last < lo || last > hi)) continue;
|
|
302
|
-
// The most recent exposure is the one worth describing: it is where the key
|
|
303
|
-
// is now, not where it was first noticed.
|
|
304
|
-
// Defensive even though `readLeakRecord` already sanitises: this is
|
|
305
|
-
// exported and takes whatever a caller hands it.
|
|
306
|
-
const seen = f.sightings?.reduce((latest, candidate) => {
|
|
307
|
-
if (!latest) return candidate;
|
|
308
|
-
return (ts(candidate.at) ?? 0) >= (ts(latest.at) ?? 0) ? candidate : latest;
|
|
309
|
-
}, undefined as LeakFinding["sightings"][number] | undefined);
|
|
310
|
-
out.push({
|
|
311
|
-
id: f.id,
|
|
312
|
-
display: f.fingerprint?.display ?? "[credential]",
|
|
313
|
-
label: f.fingerprint?.label ?? "secret",
|
|
314
|
-
length: f.fingerprint?.length ?? 0,
|
|
315
|
-
attributed: f.fingerprint?.attributed === true,
|
|
316
|
-
name: f.name,
|
|
317
|
-
confidence: f.confidence,
|
|
318
|
-
first_seen: f.firstSeen,
|
|
319
|
-
last_seen: f.lastSeen,
|
|
320
|
-
occurrences: f.occurrences,
|
|
321
|
-
cli: seen?.cli ?? "unknown",
|
|
322
|
-
project: seen?.cwd ?? "unknown",
|
|
323
|
-
mechanism: seen?.mechanism?.summary ?? "seen in a transcript",
|
|
324
|
-
direction: seen?.mechanism?.direction ?? "result",
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
// Most recently seen first: the digest's first row should be the one whose
|
|
328
|
-
// key is most likely still in use.
|
|
329
|
-
return out.sort((a, b) => (ts(b.last_seen) ?? 0) - (ts(a.last_seen) ?? 0));
|
|
330
|
-
}
|
|
331
|
-
|
|
332
229
|
export function buildHarmReport(
|
|
333
230
|
result: AuditResult,
|
|
334
231
|
lastReportedAt: string | undefined,
|
|
335
232
|
intervalDays: number,
|
|
336
|
-
leakFindings: LeakFinding[] = [],
|
|
337
233
|
): HarmReport {
|
|
338
234
|
const to = new Date(Date.parse(result.scannedAt));
|
|
339
235
|
const windowTo = Number.isFinite(to.getTime()) ? to : new Date();
|
|
@@ -360,8 +256,5 @@ export function buildHarmReport(
|
|
|
360
256
|
window_from: from.toISOString(),
|
|
361
257
|
window_to: windowTo.toISOString(),
|
|
362
258
|
harmful: selectHarmful(result, from, windowTo, { includeUnplaceable: isFirstReport }),
|
|
363
|
-
// One email, one actionable credential. Sending every row turns a leak
|
|
364
|
-
// alert into a backlog digest and obscures the event that just happened.
|
|
365
|
-
leaks: selectLeaks(leakFindings, from, windowTo).slice(0, 1),
|
|
366
259
|
};
|
|
367
260
|
}
|