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
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,86616,29054,e=>{"use strict";function t(e,a={},s=15e3){let n=AbortSignal.timeout(s),i=a.signal?AbortSignal.any([a.signal,n]):n;return fetch(e,{...a,signal:i})}function a(e){if(null===e||"object"!=typeof e)return!1;let t=e.name;return"AbortError"===t||"TimeoutError"===t}e.s(["fetchWithTimeout",0,t,"isAbortError",0,a],29054);class s extends Error{kind;constructor(e,t){super(t),this.kind=e,this.name="RerunError"}}async function n(e){try{let a=await t("/api/audit/run",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({cli:e.cli.length>0?e.cli:void 0,since:"all"===e.since?void 0:e.since,noCache:!!e.noCache||void 0})});if(!a.ok&&409!==a.status){let e=await a.text().catch(()=>"");throw console.error("audit run failed:",a.status,e),new s("post_failed",`audit run failed (${a.status})`)}}catch(e){if(e instanceof s)throw e;throw console.error("audit run request failed:",e),new s(a(e)?"timeout":"network","audit run request failed")}let n=0;for(;;){await new Promise(e=>setTimeout(e,1e3));let e=null;try{let a=await t("/api/audit/status",{cache:"no-store"});a.ok&&(e=await a.json())}catch{}if(null===e){if(++n>=10)throw new s("network","audit status poll lost the server");continue}if(n=0,!e.running){if(e.error)throw new s("post_failed",e.error);return}}}e.s(["RerunError",0,s,"triggerRun",0,n],86616)},93237,e=>{"use strict";var t=e.i(43476),a=e.i(71645),s=e.i(95187);let n=(0,s.createServerReference)("0010d731e46f24bbaee74596f787e10879803125f1",s.callServer,void 0,s.findSourceMapURL,"getScheduledAuditAction"),i=(0,s.createServerReference)("4072a14fe91ce270d2518518491fa1971e194fedee",s.callServer,void 0,s.findSourceMapURL,"setAutoAuditAction"),l=(0,s.createServerReference)("403a11adb1c6381625711f1c9696b58c97cf602e2d",s.callServer,void 0,s.findSourceMapURL,"setAuditNotifyAction"),r=(0,s.createServerReference)("40cffc801944f1a09aa2865a3f3c380212656e17ec",s.callServer,void 0,s.findSourceMapURL,"setAuditIntervalAction");var o=e.i(86616),c=e.i(12344),d=e.i(29054);let u=/^[^\s@]+@[^\s@]+\.[^\s@]+$/;function h(e){if((0,d.isAbortError)(e))return"request timed out. check your network and try again.";let t=e instanceof Error?e.message:String(e);return`network error: ${t}`}function m({open:e,headline:s="where to route the reminder?",subhead:n="we'll send a one-time code to confirm.",onClose:i,onAuthed:l,source:r="unknown"}){let{capture:o}=(0,c.usePostHog)(),[f,b]=(0,a.useState)({kind:"email",error:null}),[p,y]=(0,a.useState)(!1),v=(0,a.useRef)(!1),x=(0,a.useRef)(null),w=(0,a.useRef)(null);(0,a.useEffect)(()=>{e&&(b({kind:"email",error:null}),y(!1),v.current=!1,o("audit_auth_dialog_opened",{source:r}))},[o,e,r]);let g=(0,a.useRef)(!1);(0,a.useEffect)(()=>{if(e){g.current=!0;return}g.current&&!v.current&&o("audit_auth_dialog_dismissed",{source:r,step:f.kind}),g.current=!1},[o,e,r,f.kind]),(0,a.useEffect)(()=>{if(!e)return;let t=setTimeout(()=>{"email"===f.kind?x.current?.focus():"code"===f.kind&&w.current?.focus()},50);return()=>clearTimeout(t)},[e,f.kind]),(0,a.useEffect)(()=>{if(!e)return;let t=e=>{"Escape"!==e.key||p||i()};return window.addEventListener("keydown",t),()=>window.removeEventListener("keydown",t)},[e,p,i]);let j="code"===f.kind&&f.resendIn>0;(0,a.useEffect)(()=>{if(!j)return;let e=setInterval(()=>{b(e=>"code"===e.kind?{...e,resendIn:Math.max(0,e.resendIn-1)}:e)},1e3);return()=>clearInterval(e)},[j]);let k=(0,a.useCallback)(async(e,t={})=>{let{isResend:a=!1}=t,s=e=>{a?b(t=>"code"===t.kind?{...t,error:e}:t):b({kind:"email",error:e})};y(!0);try{let t=await (0,d.fetchWithTimeout)("/api/auth/login-request",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({email:e})}),a=await t.json().catch(()=>({}));if(!t.ok){let e=a.message??"could not send code.";"rate_limited"===a.code&&void 0!==a.retry_after_secs?e=`too many tries. wait ${a.retry_after_secs}s and try again.`:"upstream_unreachable"===a.code&&(e="api-server unreachable. check your network."),s(e);return}b({kind:"code",email:e,error:null,expiresIn:a.expires_in??600,resendIn:a.resend_available_in??30})}catch(e){s(h(e))}finally{y(!1)}},[]),N=(0,a.useCallback)(async(e,t)=>{y(!0);try{let a=await (0,d.fetchWithTimeout)("/api/auth/login-verify",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({email:e,code:t})}),s=await a.json().catch(()=>({}));if(!a.ok||!s.authenticated||!s.user){let e=s.message??"invalid code.";"invalid_code"===s.code&&(e="wrong code, or it expired. try again."),b(t=>"code"===t.kind?{...t,error:e}:t);return}v.current=!0,o("audit_auth_dialog_succeeded",{source:r}),b({kind:"done",user:s.user}),l(s.user)}catch(t){let e=h(t);b(t=>"code"===t.kind?{...t,error:e}:t)}finally{y(!1)}},[o,l,r]),S=(0,a.useCallback)(async e=>{if(e.preventDefault(),p||"email"!==f.kind)return;let t=String(new FormData(e.currentTarget).get("email")??"").trim().toLowerCase();u.test(t)?await k(t):b({kind:"email",error:"that doesn't look like an email."})},[p,f,k]),C=(0,a.useCallback)(async e=>{if(e.preventDefault(),p||"code"!==f.kind)return;let t=String(new FormData(e.currentTarget).get("code")??"").trim();t.length<4||t.length>12?b(e=>"code"===e.kind?{...e,error:"code is 4–12 characters."}:e):await N(f.email,t)},[p,f,N]),M=(0,a.useCallback)(async()=>{"code"!==f.kind||f.resendIn>0||p||await k(f.email,{isResend:!0})},[f,p,k]);return e?(0,t.jsx)("div",{className:"auth-dialog-backdrop",role:"dialog","aria-modal":"true","aria-labelledby":"auth-dialog-title",onClick:e=>{p||e.target!==e.currentTarget||i()},children:(0,t.jsxs)("div",{className:"auth-dialog",children:[(0,t.jsx)("button",{type:"button",className:"auth-close",onClick:i,disabled:p,"aria-label":"close",children:"×"}),(0,t.jsx)("h2",{id:"auth-dialog-title",className:"auth-headline",children:s}),"email"===f.kind&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("p",{className:"auth-sub",children:n}),(0,t.jsxs)("form",{onSubmit:S,className:"auth-form",children:[(0,t.jsx)("input",{ref:x,id:"auth-dialog-email",name:"email",type:"email",autoComplete:"email",inputMode:"email",spellCheck:!1,placeholder:"you@yourdomain.com",disabled:p,className:"auth-input",required:!0}),f.error&&(0,t.jsx)("div",{className:"auth-error",children:f.error}),(0,t.jsxs)("div",{className:"auth-actions",children:[(0,t.jsx)("button",{type:"submit",className:"auth-btn primary",disabled:p,children:p?"sending…":"send code"}),(0,t.jsx)("button",{type:"button",className:"auth-btn",onClick:i,disabled:p,children:"cancel"})]})]})]}),"code"===f.kind&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("p",{className:"auth-sub",children:["code sent to ",(0,t.jsx)("span",{className:"auth-email",children:f.email}),". expires in ",Math.ceil(f.expiresIn/60)," min."]}),(0,t.jsxs)("form",{onSubmit:C,className:"auth-form",children:[(0,t.jsx)("input",{ref:w,id:"auth-dialog-code",name:"code",type:"text",inputMode:"numeric",autoComplete:"one-time-code",spellCheck:!1,placeholder:"123456",disabled:p,className:"auth-input auth-input-code",maxLength:12,required:!0}),f.error&&(0,t.jsx)("div",{className:"auth-error",children:f.error}),(0,t.jsxs)("div",{className:"auth-actions",children:[(0,t.jsx)("button",{type:"submit",className:"auth-btn primary",disabled:p,children:p?"verifying…":"verify"}),(0,t.jsx)("button",{type:"button",className:"auth-btn",onClick:M,disabled:p||f.resendIn>0,children:f.resendIn>0?`resend in ${f.resendIn}s`:"resend code"})]}),(0,t.jsx)("button",{type:"button",className:"auth-back",onClick:()=>b({kind:"email",error:null}),disabled:p,children:"← use a different email"})]})]}),"done"===f.kind&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("p",{className:"auth-sub",children:[(0,t.jsx)("span",{className:"auth-ok",children:"✓"})," signed in as"," ",(0,t.jsx)("span",{className:"auth-email",children:f.user.email}),"."]}),(0,t.jsx)("div",{className:"auth-actions",children:(0,t.jsx)("button",{type:"button",className:"auth-btn primary",onClick:i,children:"continue"})})]})]})}):null}var f=e.i(55574),b=e.i(34713);function p(e){return new Date(e).toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}function y(e,t){let a=e-t;if(a<=0)return"now";let s=Math.floor(a/864e5),n=Math.floor(a%864e5/36e5);return s>0?n>0?`${s}d ${n}h`:`${s}d`:n>0?`${n}h`:`${Math.max(1,Math.floor(a/6e4))}m`}function v(e){return e.toLocaleString("en-US")}function x({label:e,value:a,sub:s,tone:n}){return(0,t.jsxs)("div",{className:"set-stat",children:[(0,t.jsx)("div",{className:"set-stat-label",children:e}),(0,t.jsx)("div",{className:`set-stat-value${n?` ${n}`:""}`,children:a}),(0,t.jsx)("div",{className:"set-stat-sub",children:s??" "})]})}function w({lastRunAtMs:e,nextDueAtMs:a,now:s}){if(null==e||null==a||a<=e)return null;let n=Math.min(100,Math.max(0,(s-e)/(a-e)*100));return(0,t.jsxs)("div",{className:"tape","aria-hidden":"true",children:[(0,t.jsxs)("div",{className:"tape-rail",children:[(0,t.jsx)("div",{className:"tape-fill",style:{width:`${n}%`}}),(0,t.jsx)("div",{className:"tape-now",style:{left:`${n}%`}})]}),(0,t.jsxs)("div",{className:"tape-ends",children:[(0,t.jsxs)("span",{children:["last scan · ",p(e)]}),(0,t.jsxs)("span",{className:"tape-next",children:["next · ",y(a,s)]})]})]})}function g({enabled:e,onChange:a,disabled:s,label:n}){return(0,t.jsx)("button",{type:"button",role:"switch","aria-checked":e,"aria-label":n,disabled:s,onClick:a,className:"sw","data-on":e?"true":"false",children:(0,t.jsx)("span",{className:"sw-knob"})})}let j=[{label:"reads",body:"every session transcript on disk — your prompts, the files your agents read and wrote, and command output."},{label:"runs",body:"entirely on this machine. the transcripts never leave it."},{label:"sends",body:"only the newest masked credential exposure, its CLI, time, location, and this machine's name — only when email is configured."}];e.s(["default",0,function({initial:e}){let[s,c]=(0,a.useState)(e),[d,u]=(0,a.useState)(e?.auto??!1),[h,k]=(0,a.useState)(e?.notify??!0),[N,S]=(0,a.useState)(e?.intervalDays??7),[C,M]=(0,a.useState)(!1),[$,A]=(0,a.useState)(!1),[E,R]=(0,a.useState)(!1),[D,_]=(0,a.useState)(null===e),[T,I]=(0,a.useState)(0),L=(0,a.useRef)(!0),F=(0,a.useRef)(null!==e),O=(0,a.useCallback)(async()=>{try{let e=await n();if(!L.current)return;c(e),F.current=!0,u(e.auto),k(e.notify),S(e.intervalDays),I(Date.now()),_(!1)}catch{L.current&&!F.current&&_(!0)}},[]);(0,a.useEffect)(()=>(L.current=!0,O(),()=>{L.current=!1}),[O]),(0,a.useEffect)(()=>{let e=()=>void O();return window.addEventListener("focus",e),document.addEventListener("visibilitychange",e),()=>{window.removeEventListener("focus",e),document.removeEventListener("visibilitychange",e)}},[O]);let P=s?.signedInAs??null,U=s?.daemon==="running",q=s?.daemon==="unsupported-platform",B=s?.schedule??null,J=s?.lastScan??null,W=B?.lastExitCode!=null&&0!==B.lastExitCode&&75!==B.lastExitCode,K=(0,a.useCallback)(async()=>{M(!0);try{let e=await i(!0);u(e.auto),(0,f.toast)("scheduled audits on."),await O()}catch{u(!1),(0,f.toast)("could not turn that on.")}finally{M(!1)}},[O]),H=(0,a.useCallback)(async()=>{if(d){M(!0);try{let e=await i(!1);e.ok&&u(e.auto),(0,f.toast)("scheduled audits off."),await O()}catch{(0,f.toast)("could not turn that off.")}finally{M(!1)}return}await K()},[d,K,O]),z=(0,a.useCallback)(async()=>{let e=!h;M(!0);try{let t=await l(e);k(t.notify),c(e=>e?{...e,notify:t.notify}:e),(0,f.toast)(t.notify?"system notifications on.":"system notifications off.")}catch{(0,f.toast)(`could not turn system notifications ${e?"on":"off"}.`)}finally{M(!1)}},[h]),G=(0,a.useCallback)(async e=>{M(!0);try{let t=await r(e);S(t.intervalDays),c(e=>e?{...e,intervalDays:t.intervalDays}:e),(0,f.toast)(`scanning every ${t.intervalDays} day${1===t.intervalDays?"":"s"}.`)}catch{S(s?.intervalDays??7),(0,f.toast)("could not save that.")}finally{M(!1)}},[s?.intervalDays]),Q=(0,a.useCallback)(async()=>{M(!0);try{await fetch("/api/auth/logout",{method:"POST"}),(0,f.toast)("signed out. scans and desktop notifications continue; email alerts pause."),await O()}catch{(0,f.toast)("could not sign out.")}finally{M(!1)}},[O]),V=(0,a.useCallback)(async()=>{if(!$){A(!0);try{await (0,o.triggerRun)({cli:[],since:"all",noCache:!1}),(0,f.toast)("scan complete."),await O()}catch(e){(0,f.toast)(e instanceof o.RerunError&&"timeout"===e.kind?"the scan is taking a while — it will finish in the background.":"the scan could not be completed.")}finally{A(!1)}}},[O,$]),X=(0,a.useCallback)(async e=>{R(!1),await O(),await K()},[K,O]),Y=s?U?{value:"running",sub:null!=s.daemonStartedAtMs&&T>0?`up ${function(e){let t=Math.floor(e/864e5);if(t>0)return`${t}d`;let a=Math.floor(e/36e5);return a>0?`${a}h`:`${Math.max(1,Math.floor(e/6e4))}m`}(Math.max(0,T-s.daemonStartedAtMs))}`:"",tone:"ok"}:q?{value:"unavailable",sub:"not on this platform"}:"not-installed"===s.daemon?{value:"not installed",sub:"run failproofai config",tone:"warn"}:"unknown"===s.daemon?{value:"unknown",sub:"could not read it"}:{value:"stopped",sub:"run failproofai config",tone:"warn"}:{value:"…",sub:"reading"},Z=d?B?.nextDueAtMs==null?{value:"pending",sub:"after the next scan"}:{value:T>0?y(B.nextDueAtMs,T):"—",sub:p(B.nextDueAtMs)}:{value:"off",sub:"nothing scheduled"},ee=J?{value:(0,b.formatRelativeTime)(Date.parse(J.finishedAt)),sub:null!=J.sessionsScanned?`${v(J.sessionsScanned)} sessions`:"scanned"}:{value:"none yet",sub:"run one below"},et=J?null==J.findings?{value:"—",sub:"unreadable"}:{value:v(J.findings),sub:"this scan",...J.findings>0?{tone:"warn"}:{}}:{value:"—",sub:"no scan yet"};return(0,t.jsxs)("main",{className:"report",children:[(0,t.jsxs)("section",{className:"section set-sec",children:[(0,t.jsxs)("div",{className:"set-mast",children:[(0,t.jsx)("h1",{className:"set-title",children:"settings"}),(0,t.jsx)("p",{className:"set-lede",children:"keeping watch, so you don't have to."})]}),D?(0,t.jsx)("div",{className:"set-console",children:(0,t.jsx)("div",{className:"set-cell",children:(0,t.jsxs)("p",{className:"set-warn",children:["could not read this machine's settings. check that the dashboard can reach ",(0,t.jsx)("code",{children:"~/.failproofai/config.json"}),"."]})})}):(0,t.jsxs)("div",{className:"set-console",children:[(0,t.jsxs)("div",{className:"set-stats",children:[(0,t.jsx)(x,{label:"daemon",...Y}),(0,t.jsx)(x,{label:"next scan",...Z}),(0,t.jsx)(x,{label:"last scan",...ee}),(0,t.jsx)(x,{label:"findings",...et})]}),(0,t.jsxs)("div",{className:"set-cols",children:[(0,t.jsxs)("div",{className:"set-cell set-config",children:[(0,t.jsx)("h2",{className:"set-cell-title",children:"scheduled audit"}),(0,t.jsxs)("div",{className:"set-row set-row-main",children:[(0,t.jsx)(g,{enabled:d,disabled:C||null===s,onChange:()=>void H(),label:d?"turn off scheduled audits":"turn on scheduled audits"}),(0,t.jsxs)("div",{className:"set-row-copy",children:[(0,t.jsx)("span",{className:"set-strong",children:d?"scanning on a schedule.":"scan on a schedule."}),(0,t.jsx)("span",{className:"set-dim",children:d?P?"scheduled scans are active; the latest masked leak is emailed.":"scheduled scans are active. add an email below for masked alerts.":"off. nothing runs and nothing is sent."})]})]}),(0,t.jsxs)("div",{className:"set-row set-row-interval",children:[(0,t.jsx)("span",{className:"set-dim",children:"scan every"}),(0,t.jsx)("input",{type:"number",className:"set-num",min:1,max:90,value:N,disabled:C||null===s,"aria-label":"days between scheduled scans",onChange:e=>S(Number(e.target.value)),onBlur:e=>{let t=Number(e.target.value);Number.isFinite(t)?t!==s?.intervalDays&&G(t):S(s?.intervalDays??7)}}),(0,t.jsx)("span",{className:"set-dim",children:"days"}),(0,t.jsxs)("span",{className:"set-hint",children:[1,"–",90]})]}),(0,t.jsxs)("div",{className:"set-row set-row-main",children:[(0,t.jsx)(g,{enabled:h,disabled:C||null===s,onChange:()=>void z(),label:h?"turn off system notifications":"turn on system notifications"}),(0,t.jsxs)("div",{className:"set-row-copy",children:[(0,t.jsx)("span",{className:"set-strong",children:"system notifications."}),(0,t.jsx)("span",{className:"set-dim",children:h?"on when a scheduled scan finds credentials.":"off. scheduled scans and email alerts continue."})]})]}),(0,t.jsx)("div",{className:"set-rule"}),(0,t.jsx)("div",{className:"set-row set-row-identity",children:P?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("span",{className:"set-dim",children:"reports go to"}),(0,t.jsx)("span",{className:"set-email",children:P.email}),(0,t.jsx)("button",{type:"button",className:"set-link",disabled:C,onClick:()=>void Q(),children:"sign out"})]}):d?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("span",{className:"set-warn",children:"signed out — scheduled scans continue; email alerts are paused."}),(0,t.jsx)("button",{type:"button",className:"set-link",disabled:C,onClick:()=>R(!0),children:"add email alerts"})]}):(0,t.jsx)("span",{className:"set-dim",children:"scans and system notifications work without an account. add email later if you want alerts in your inbox."})}),d&&s&&!U&&(0,t.jsx)("p",{className:"set-warn",children:q?"the background service isn't available on this platform, so scheduled scans can't run here. you can still run one now.":"not-installed"===s.daemon?"the background service isn't installed, so nothing will run on the timer. install it with `failproofai config`.":"the background service is stopped, so nothing will run until it starts. repair it with `failproofai config`."}),W&&(0,t.jsxs)("p",{className:"set-warn",children:["the last scheduled scan exited ",B?.lastExitCode,". the next one will still run."]}),(0,t.jsx)("div",{className:"set-actions",children:(0,t.jsx)("button",{type:"button",className:"btn btn-press",disabled:$,onClick:()=>void V(),children:$?"[ scanning… ]":"[ run a scan now ]"})})]}),(0,t.jsxs)("div",{className:"set-cell set-how",children:[(0,t.jsx)("h2",{className:"set-cell-title",children:"how it works"}),(0,t.jsx)("dl",{className:"set-how-list",children:j.map(e=>(0,t.jsxs)("div",{className:"set-how-row",children:[(0,t.jsx)("dt",{className:"set-how-label",children:e.label}),(0,t.jsx)("dd",{className:"set-how-body",children:e.body})]},e.label))})]})]})]}),d&&T>0&&(0,t.jsx)(w,{lastRunAtMs:B?.lastRunAtMs??null,nextDueAtMs:B?.nextDueAtMs??null,now:T})]}),(0,t.jsx)(m,{open:E,source:"settings_scheduled_audit",headline:"where should the report go?",subhead:"we'll send a one-time code to confirm.",onClose:()=>R(!1),onAuthed:e=>void X(e)})]})}],93237)},34713,e=>{"use strict";e.s(["formatDuration",0,function(e){if(e<1e3)return`${e}ms`;let t=Math.round(e/100);if(t<600)return`${(t/10).toFixed(1)}s`;let a=Math.round(e/1e3);if(a<3600){let e=Math.floor(a/60);return`${e}m ${a%60}s`}let s=Math.round(e/6e4),n=Math.floor(s/60);return`${n}h ${s%60}m`},"formatRelativeTime",0,function(e){let t=Date.now()-e;return t<6e4?`${Math.max(1,Math.floor(t/1e3))}s ago`:t<36e5?`${Math.floor(t/6e4)}m ago`:t<864e5?`${Math.floor(t/36e5)}h ago`:`${Math.floor(t/864e5)}d ago`}])}]);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,24358,e=>{"use strict";var s=e.i(43476),a=e.i(71645),n=e.i(95187);let t=(0,n.createServerReference)("0000e73cd773fa2c53602d9adf5b270fa497747e54",n.callServer,void 0,n.findSourceMapURL,"getAuditResultAction");var r=e.i(12344);function i({cachedAt:e,fixed:a=!1}){return(0,s.jsxs)("footer",{className:a?"report-footer report-footer--fixed":"report-footer",children:[(0,s.jsx)("img",{src:"/logo.svg",alt:"failproof_ai",style:{height:18,display:"inline-block",verticalAlign:"middle"}}),(0,s.jsx)("span",{style:{margin:"0 12px",color:"var(--line-2)"},children:"·"}),"audit v1.0",(0,s.jsx)("span",{style:{margin:"0 12px",color:"var(--line-2)"},children:"·"}),"generated ",function(e){if(!e)return"—";let s=new Date(e);if(Number.isNaN(s.getTime()))return"—";let a=s.getUTCDate().toString().padStart(2,"0"),n=["jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"][s.getUTCMonth()],t=s.getUTCFullYear(),r=s.getUTCHours().toString().padStart(2,"0"),i=s.getUTCMinutes().toString().padStart(2,"0");return`${a} ${n} ${t}, ${r}:${i} utc`}(e),(0,s.jsx)("span",{style:{margin:"0 12px",color:"var(--line-2)"},children:"·"}),(0,s.jsx)("span",{style:{color:"var(--ink-2)"},children:"auto-healing for your agents."})]})}let l=(0,n.createServerReference)("40b4a6c8d1e4b511e816c8d7493f24fb0e7e7be2e6",n.callServer,void 0,n.findSourceMapURL,"dismissLeakAction"),c=(0,n.createServerReference)("006f2aa64f86bf0154079fad166e1e5e06b1d050e5",n.callServer,void 0,n.findSourceMapURL,"getLeaksAction"),d=(0,n.createServerReference)("40a7972103d740f0429a2a6110f5ac4613c7847631",n.callServer,void 0,n.findSourceMapURL,"setLeakNotifyAction");function o(e){let s=Date.parse(e);if(!Number.isFinite(s))return"unknown";let a=Math.floor((Date.now()-s)/864e5);return a<=0?"today":1===a?"yesterday":a<30?`${a}d ago`:new Date(s).toLocaleDateString(void 0,{month:"short",day:"numeric"})}function u(){let[e,n]=(0,a.useState)(null),[t,r]=(0,a.useState)(!0),[i,u]=(0,a.useState)(null),h=(0,a.useCallback)(async()=>{let e=await c();n(e.rows),r(e.notify)},[]);(0,a.useEffect)(()=>{h()},[h]);let m=(0,a.useCallback)(async e=>{u(e);try{await l(e)&&n(s=>s?.filter(s=>s.id!==e)??null)}finally{u(null)}},[]),p=(0,a.useCallback)(async()=>{let e=!t;r(e),await d(e)||r(!e)},[t]);return null===e?null:(0,s.jsxs)("section",{className:"audit-sec","data-screen-label":"02 Leaks",children:[(0,s.jsxs)("div",{className:"audit-sec-head",children:[(0,s.jsxs)("span",{className:"audit-sec-eyebrow",children:[(0,s.jsx)("span",{className:"ix",children:"02"}),"// credentials"]}),(0,s.jsx)("button",{type:"button",className:"cbb-toggle leak-notify","data-on":t,onClick:p,"aria-label":t?"turn desktop notifications off":"turn desktop notifications on",title:"desktop notification when a new credential is found",children:(0,s.jsx)("span",{className:"cbb-toggle-knob"})})]}),0===e.length?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)("h2",{className:"audit-sec-title",children:"no credentials found"}),(0,s.jsxs)("div",{className:"audit-sec-sub",children:["// nothing in this machine's transcripts matched a known key format",(0,s.jsx)("br",{}),"// or a secret-named assignment."]})]}):(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)("h2",{className:"audit-sec-title",children:[e.length," credential",1===e.length?"":"s"," in your transcripts"]}),(0,s.jsx)("div",{className:"audit-sec-sub",children:"// one row per distinct key — an exposure count is not a key count."}),(0,s.jsx)("ul",{className:"leak-list",children:e.map(e=>(0,s.jsxs)("li",{className:"leak-row","data-attributed":e.attributed,children:[(0,s.jsxs)("div",{className:"leak-row-top",children:[(0,s.jsx)("code",{className:"leak-key",children:e.display}),(0,s.jsx)("span",{className:"leak-label",children:e.label}),e.name?(0,s.jsx)("span",{className:"leak-name",children:e.name}):null,(0,s.jsx)("button",{type:"button",className:"leak-dismiss",onClick:()=>void m(e.id),disabled:i===e.id,children:"not a secret"})]}),(0,s.jsxs)("div",{className:"leak-how",children:[(0,s.jsx)("span",{className:"leak-verb",children:e.mechanism})," in ",(0,s.jsx)("span",{className:"leak-where",children:e.project})," via ",(0,s.jsx)("span",{className:"leak-who",children:e.cli})," · leaked ",(0,s.jsx)("span",{className:"leak-when",children:o(e.firstSeen)}),e.firstSeen!==e.lastSeen?(0,s.jsxs)(s.Fragment,{children:[" · last seen ",(0,s.jsx)("span",{className:"leak-when",children:o(e.lastSeen)})]}):null]}),(0,s.jsxs)("div",{className:"leak-foot",children:[(0,s.jsxs)("span",{className:"leak-meta",children:[e.occurrences," exposure",1===e.occurrences?"":"s",e.sessions>1?` \xb7 ${e.sessions} sessions`:""," · ",e.length," chars"]}),(0,s.jsx)("span",{className:"leak-dir",children:"input"===e.direction?"sent by the agent — blockable at PreToolUse":"returned to the agent — already in the transcript"}),(0,s.jsx)("span",{className:"leak-fix",children:e.attributed?`rotate it in the ${e.label.replace(/ (API )?(key|token).*$/i,"")} console`:e.name?`find what reads ${e.name}, then rotate it there`:"trace it to its owner, then rotate it"})]})]},e.id))})]})]})}var h=e.i(86616);function m({mode:e,running:a,onStarted:n,onCompleted:t}){let{capture:i}=(0,r.usePostHog)(),l=async()=>{i("audit_first_run_clicked",{source:"empty_state",mode:e}),n();try{await (0,h.triggerRun)({cli:[],since:"all"})}catch(e){console.error("audit first run failed:",e)}finally{await t()}};return"no-cache"===e?(0,s.jsxs)("section",{className:"section empty-section","data-screen-label":"00 Empty",children:[(0,s.jsxs)("div",{className:"section-mast",children:[(0,s.jsxs)("div",{className:"section-label",children:[(0,s.jsx)("span",{className:"glyph",children:"━━"})," audit"," ",(0,s.jsx)("span",{className:"sep",children:"·"})," first run"]}),(0,s.jsxs)("div",{className:"section-meta",children:[(0,s.jsx)("span",{style:{color:"var(--dim)"},children:"○"})," no cache yet"]})]}),(0,s.jsx)("h2",{className:"section-h",children:"scan and see."}),(0,s.jsxs)("div",{className:"panel empty-panel",children:[(0,s.jsxs)("div",{className:"empty-glyph","aria-hidden":"true",children:[(0,s.jsx)("div",{className:"empty-glyph-grid",children:Array.from({length:36}).map((e,a)=>{let n=["1","5","8","10","13","17","20","23","25","30","32"].includes(String(a));return(0,s.jsx)("span",{className:"px"+(n?" on":"")},a)})}),(0,s.jsx)("div",{className:"empty-glyph-label",children:"▮▮ no audit data yet"})]}),(0,s.jsx)("h3",{className:"empty-headline",children:"run your first audit."}),(0,s.jsxs)("p",{className:"empty-sub",children:["we'll walk every transcript across your installed CLIs — Claude Code, Codex, Copilot, Cursor, OpenCode, Pi — and count every wasteful","or risky action. you'll get a leak report and a scan summary."]}),(0,s.jsxs)("div",{className:"empty-actions",children:[(0,s.jsx)("button",{type:"button",className:"btn btn-primary btn-press empty-cta",onClick:l,disabled:a,children:a?"[ scanning… ]":"[ run audit ]"}),(0,s.jsx)("span",{className:"empty-meta",children:"scans all sessions · all installed CLIs · may take a while"})]})]})]}):(0,s.jsxs)("section",{className:"section empty-section","data-screen-label":"00 Empty",children:[(0,s.jsxs)("div",{className:"section-mast",children:[(0,s.jsxs)("div",{className:"section-label",children:[(0,s.jsx)("span",{className:"glyph",children:"━━"})," audit"," ",(0,s.jsx)("span",{className:"sep",children:"·"})," zero transcripts"]}),(0,s.jsxs)("div",{className:"section-meta",children:[(0,s.jsx)("span",{style:{color:"var(--amber)"},children:"●"})," hooks not installed"]})]}),(0,s.jsx)("h2",{className:"section-h",children:"nothing to read."}),(0,s.jsxs)("div",{className:"panel empty-panel",children:[(0,s.jsxs)("div",{className:"empty-glyph","aria-hidden":"true",children:[(0,s.jsx)("div",{className:"empty-glyph-grid",children:Array.from({length:36}).map((e,a)=>{let n=["2","4","9","11","16","18","23","25","27","30","33"].includes(String(a));return(0,s.jsx)("span",{className:"px"+(n?" on":"")},a)})}),(0,s.jsx)("div",{className:"empty-glyph-label",children:"▮▮ no sessions found"})]}),(0,s.jsx)("h3",{className:"empty-headline",children:"install hooks first."}),(0,s.jsx)("p",{className:"empty-sub",children:"failproofai couldn't find any transcripts to scan on this machine. install the hooks for at least one CLI and come back."}),(0,s.jsxs)("div",{className:"empty-actions",children:[(0,s.jsx)("a",{href:"https://docs.befailproof.ai/getting-started",target:"_blank",rel:"noopener noreferrer",className:"btn btn-primary btn-press empty-cta",onClick:()=>{i("audit_install_guide_clicked",{source:"empty_state",mode:e})},children:"[ install guide → ]"}),(0,s.jsx)("span",{className:"empty-meta",children:"takes about 30 seconds · one command per CLI"})]})]})]})}let p=[{label:"discovering transcripts",detail:"walking ~/.claude, ~/.codex, ~/.cursor, …"},{label:"parsing session logs",detail:"reading JSONL + sqlite session stores"},{label:"running policy checks",detail:"replaying every policy against each tool call"},{label:"aggregating results",detail:"counting hits, ranking by frequency"}];function g(){let[e,n]=(0,a.useState)(0),[t,r]=(0,a.useState)(0);(0,a.useEffect)(()=>{let e=setInterval(()=>{n(e=>Math.min(e+1,p.length-1))},4e3),s=setInterval(()=>r(e=>(e+1)%4),350);return()=>{clearInterval(e),clearInterval(s)}},[]);let i=".".repeat(t+1),l=e===p.length-1,c=Math.round(100*Math.min(.9,(e+1)/p.length*.9));return(0,s.jsxs)("section",{className:"section running-section","data-screen-label":"00 Running",children:[(0,s.jsxs)("div",{className:"section-mast",children:[(0,s.jsxs)("div",{className:"section-label",children:[(0,s.jsx)("span",{className:"glyph",children:"━━"})," audit"," ",(0,s.jsx)("span",{className:"sep",children:"·"})," in progress"]}),(0,s.jsxs)("div",{className:"section-meta",children:[(0,s.jsx)("span",{style:{color:"var(--accent-pink)"},children:"●"})," scanning"]})]}),(0,s.jsxs)("h2",{className:"section-h",children:["scanning sessions",i]}),(0,s.jsxs)("div",{className:"panel running-panel",children:[(0,s.jsxs)("div",{className:"running-header",children:[(0,s.jsx)("span",{className:"running-prompt",children:"$"}),(0,s.jsx)("span",{className:"running-cmd",children:"failproofai audit"}),(0,s.jsx)("span",{className:"running-cursor","aria-hidden":"true",children:"▮"})]}),(0,s.jsx)("ul",{className:"running-stages",children:p.map((a,n)=>{let r=n<e,i=n===e;return(0,s.jsxs)("li",{className:"running-stage"+(r?" done":i?" active":" queued"),children:[(0,s.jsx)("span",{className:"running-marker","aria-hidden":"true",children:r?"✓":i?"▮▮":"○"}),(0,s.jsxs)("div",{className:"running-stage-body",children:[(0,s.jsx)("div",{className:"running-stage-label",children:a.label}),i&&(0,s.jsx)("div",{className:"running-stage-detail",children:l?"finishing up…":a.detail})]}),i&&(0,s.jsx)("span",{className:"running-stage-spin","aria-hidden":"true",children:["⠋","⠙","⠹","⠸"][t]})]},n)})}),(0,s.jsxs)("div",{className:"running-bar-label",children:[(0,s.jsx)("span",{children:"progress"}),(0,s.jsxs)("span",{style:{color:"var(--dim)"},children:[c,"%"]})]}),(0,s.jsx)("div",{className:"running-bar-track",children:(0,s.jsx)("div",{className:"running-bar-fill",style:{width:`${c}%`}})}),(0,s.jsx)("p",{className:"running-foot",children:"this can take a while depending on how much session history you have."})]})]})}let x={timeout:"audit took too long — try again",network:"network hiccup — check your connection",post_failed:"audit failed — try again"};function f({status:e,onDismiss:n}){let[t,r]=(0,a.useState)(()=>Date.now());if((0,a.useEffect)(()=>{if("running"!==e.kind)return;let s=setInterval(()=>r(Date.now()),1e3);return()=>clearInterval(s)},[e.kind]),"idle"===e.kind)return null;if("running"===e.kind){let a=Math.max(0,t-e.startedAt);return(0,s.jsxs)("div",{className:"audit-progress-strip",role:"status","aria-live":"polite",children:[(0,s.jsxs)("div",{className:"audit-progress-strip__inner",children:[(0,s.jsxs)("span",{className:"audit-progress-strip__label",children:[(0,s.jsx)("span",{className:"audit-progress-strip__spinner","aria-hidden":"true"}),"re-auditing your sessions"]}),(0,s.jsx)("span",{className:"audit-progress-strip__elapsed",children:function(e){if(e<0)return"00:00";let s=Math.floor(e/1e3),a=Math.floor(s/60).toString().padStart(2,"0"),n=(s%60).toString().padStart(2,"0");return`${a}:${n}`}(a)})]}),(0,s.jsx)("div",{className:"audit-progress-strip__pulse","aria-hidden":"true"})]})}return(0,s.jsx)("div",{className:"audit-progress-strip audit-progress-strip--failed",role:"alert",children:(0,s.jsxs)("div",{className:"audit-progress-strip__inner",children:[(0,s.jsxs)("span",{className:"audit-progress-strip__label",children:[(0,s.jsx)("span",{className:"audit-progress-strip__x","aria-hidden":"true",children:"×"}),x[e.reason]]}),(0,s.jsx)("button",{type:"button",className:"audit-progress-strip__dismiss",onClick:n,"aria-label":"dismiss",children:"[ dismiss ]"})]})})}function j({result:e,cachedAt:n,projectFromUrl:t,isRunning:l,rerunStatus:c,onRerun:d,onDismissRerun:o}){let{capture:h}=(0,r.usePostHog)(),{detectorsTriggered:m,missing:p}=(0,a.useMemo)(()=>{let s=0,a=0;for(let n of e.results)n.hits>0&&s++,"builtin"===n.source&&!n.enabledInConfig&&n.hits>0&&a++;return{detectorsTriggered:s,missing:a}},[e]),g=(0,a.useRef)(!1);return(0,a.useEffect)(()=>{g.current||(g.current=!0,h("audit_dashboard_viewed",{missing:p,transcripts_scanned:e.transcripts.scanned,results_count:e.results.length,detectors_triggered:m}))},[h,p,e.transcripts.scanned,e.results.length,m]),(0,s.jsxs)("div",{className:"app",children:[(0,s.jsx)(f,{status:c,onDismiss:o}),(0,s.jsxs)("div",{className:"app-shell",children:[(0,s.jsxs)("div",{className:"report",children:[(0,s.jsx)(y,{transcripts:e.transcripts.scanned,events:e.eventsScanned??0,projects:e.projectsScanned?.length??0,isRunning:l||"running"===c.kind,onRerun:()=>d("scan_header")}),(0,s.jsx)(u,{})]}),(0,s.jsx)(i,{cachedAt:n})]})]})}function y({transcripts:e,events:a,projects:n,isRunning:t,onRerun:r}){return(0,s.jsxs)("section",{className:"audit-sec","data-screen-label":"01 Scan",children:[(0,s.jsxs)("div",{className:"audit-sec-head",children:[(0,s.jsxs)("span",{className:"audit-sec-eyebrow",children:[(0,s.jsx)("span",{className:"ix",children:"01"}),"// scan"]}),(0,s.jsx)("button",{type:"button",className:"scan-rerun",onClick:r,disabled:t,children:t?"scanning…":"run again"})]}),(0,s.jsx)("h2",{className:"audit-sec-title",children:"scan complete"}),(0,s.jsxs)("div",{className:"audit-sec-sub",children:[a.toLocaleString()," tool call",1===a?"":"s"," across ",e.toLocaleString()," ","transcript",1===e?"":"s",n>0?` \xb7 ${n.toLocaleString()} project${1===n?"":"s"}`:""]}),(0,s.jsxs)("div",{className:"audit-sec-sub",children:["// the report is being rebuilt around leak detection. the scan, the",(0,s.jsx)("br",{}),"// 12-CLI transcript reader and the emailed digest are unaffected."]})]})}function N({running:e,mode:a="no-cache",rerunStatus:n,onDismissRerun:t,onStarted:r,onCompleted:l}){return(0,s.jsxs)("div",{className:"app",children:[(0,s.jsx)(f,{status:n,onDismiss:t}),(0,s.jsxs)("div",{className:"app-shell",children:[(0,s.jsx)("div",{className:"report",children:e?(0,s.jsx)(g,{}):(0,s.jsx)(m,{mode:a,running:e,onStarted:r,onCompleted:l})}),(0,s.jsx)(i,{cachedAt:null,fixed:!0})]})]})}e.s(["AuditDashboard",0,function({initial:e,projectFromUrl:n,totalCatalogSize:i}){let[l,c]=(0,a.useState)(e),[d,o]=(0,a.useState)(!1),[u,m]=(0,a.useState)({kind:"idle"}),{capture:p}=(0,r.usePostHog)(),g=(0,a.useRef)(null),x=(0,a.useRef)(!1),f="cached"===l.status?l.result.transcripts.scanned:0,y="cached"===l.status?l.result.results.length:0,b=(0,a.useCallback)(async()=>{c(await t())},[]),v=(0,a.useCallback)(async e=>{if(d)return;p("audit_rerun_clicked",{source:e,since:"all"}),o(!0);let s=Date.now();m({kind:"running",startedAt:s});try{await (0,h.triggerRun)({cli:[],since:"all",noCache:!0}),await b(),p("audit_rerun_succeeded",{source:e,since:"all",duration_ms:Date.now()-s}),m({kind:"idle"})}catch(n){let a=n instanceof h.RerunError?n.kind:"network";p("audit_rerun_failed",{kind:a,source:e,since:"all",cli_filter:"all",duration_ms:Date.now()-s}),m({kind:"failed",reason:a,failedAt:Date.now()})}finally{o(!1)}},[p,b,d]),k=(0,a.useCallback)(()=>{m({kind:"idle"})},[]);if((0,a.useEffect)(()=>(document.body.classList.add("audit-body"),()=>document.body.classList.remove("audit-body")),[]),(0,a.useEffect)(()=>{let e=d?"running":"empty"===l.status?"empty":0===f?"zero_sessions":"report";g.current!==e&&(g.current=e,p("audit_page_viewed",{state:e,has_cache:"cached"===l.status}))},[l.status,p,d,f]),(0,a.useEffect)(()=>{x.current=!1},[l.status,d,f]),(0,a.useEffect)(()=>{if(d||"cached"!==l.status||0===f)return;let e=0,s=()=>{if(e=0,x.current)return;let s=document.documentElement.scrollHeight-window.innerHeight;s<=0||window.scrollY>=s-24&&(x.current=!0,p("audit_page_scrolled_to_end",{results_count:y,transcripts_scanned:f}))},a=()=>{0===e&&(e=requestAnimationFrame(s))};return s(),window.addEventListener("scroll",a,{passive:!0}),()=>{window.removeEventListener("scroll",a),0!==e&&cancelAnimationFrame(e)}},[l.status,p,y,d,f]),"empty"===l.status&&!d)return(0,s.jsx)(N,{running:!1,rerunStatus:u,onDismissRerun:k,onStarted:()=>o(!0),onCompleted:async()=>{o(!1),await b()}});if("empty"===l.status&&d)return(0,s.jsx)(N,{running:!0,rerunStatus:u,onDismissRerun:k,onStarted:()=>{},onCompleted:async()=>{o(!1),await b()}});let w="cached"===l.status?l.result:null;if(!w)return null;let S="cached"===l.status?l.cachedAt:null;return 0===w.transcripts.scanned?(0,s.jsx)(N,{running:d,mode:"zero-sessions",rerunStatus:u,onDismissRerun:k,onStarted:()=>o(!0),onCompleted:async()=>{o(!1),await b()}}):(0,s.jsx)(j,{result:w,cachedAt:S,projectFromUrl:n,isRunning:d,rerunStatus:u,onRerun:e=>v(e),onDismissRerun:k})}],24358)},86616,29054,e=>{"use strict";function s(e,a={},n=15e3){let t=AbortSignal.timeout(n),r=a.signal?AbortSignal.any([a.signal,t]):t;return fetch(e,{...a,signal:r})}function a(e){if(null===e||"object"!=typeof e)return!1;let s=e.name;return"AbortError"===s||"TimeoutError"===s}e.s(["fetchWithTimeout",0,s,"isAbortError",0,a],29054);class n extends Error{kind;constructor(e,s){super(s),this.kind=e,this.name="RerunError"}}async function t(e){try{let a=await s("/api/audit/run",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({cli:e.cli.length>0?e.cli:void 0,since:"all"===e.since?void 0:e.since,noCache:!!e.noCache||void 0})});if(!a.ok&&409!==a.status){let e=await a.text().catch(()=>"");throw console.error("audit run failed:",a.status,e),new n("post_failed",`audit run failed (${a.status})`)}}catch(e){if(e instanceof n)throw e;throw console.error("audit run request failed:",e),new n(a(e)?"timeout":"network","audit run request failed")}let t=0;for(;;){await new Promise(e=>setTimeout(e,1e3));let e=null;try{let a=await s("/api/audit/status",{cache:"no-store"});a.ok&&(e=await a.json())}catch{}if(null===e){if(++t>=10)throw new n("network","audit status poll lost the server");continue}if(t=0,!e.running){if(e.error)throw new n("post_failed",e.error);return}}}e.s(["RerunError",0,n,"triggerRun",0,t],86616)}]);
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
"use server";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
activeFindings,
|
|
5
|
-
dismissFinding,
|
|
6
|
-
readLeakRecord,
|
|
7
|
-
} from "@/src/audit/leak-store";
|
|
8
|
-
import { readConfig, updateConfig } from "@/src/hooks/fp-config";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* One credential, flattened for the report table.
|
|
12
|
-
*
|
|
13
|
-
* A separate shape from `LeakFinding` on purpose: this crosses into a client
|
|
14
|
-
* component, so it carries only what is drawn. The finding's raw sightings, its
|
|
15
|
-
* rule and its salt stay on the server side of the boundary — not because any
|
|
16
|
-
* of them is a secret (the record holds no secret by construction), but because
|
|
17
|
-
* a display type that mirrors the storage type drifts into rendering whatever
|
|
18
|
-
* gets added to storage next.
|
|
19
|
-
*/
|
|
20
|
-
export interface LeakRow {
|
|
21
|
-
id: string;
|
|
22
|
-
/** WHAT — `ghp_••••••••4f2a`. Never the value; there is no value to send. */
|
|
23
|
-
display: string;
|
|
24
|
-
label: string;
|
|
25
|
-
length: number;
|
|
26
|
-
/** False means no console to revoke at — the advice changes completely. */
|
|
27
|
-
attributed: boolean;
|
|
28
|
-
/** The identifier it was assigned to, when there was one. */
|
|
29
|
-
name: string | null;
|
|
30
|
-
/** WHO — the harness whose transcript carried it. */
|
|
31
|
-
cli: string;
|
|
32
|
-
/** WHERE — home-shortened project path. */
|
|
33
|
-
project: string;
|
|
34
|
-
/** WHEN — ISO, formatted client-side so it lands in the reader's timezone. */
|
|
35
|
-
lastSeen: string;
|
|
36
|
-
firstSeen: string;
|
|
37
|
-
/** HOW — "read from ~/…/.env". */
|
|
38
|
-
mechanism: string;
|
|
39
|
-
/** WHY IT MATTERS — an input the agent sent can be denied next time; a
|
|
40
|
-
* result it received cannot be un-received. */
|
|
41
|
-
direction: "input" | "result";
|
|
42
|
-
occurrences: number;
|
|
43
|
-
sessions: number;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface LeaksPayload {
|
|
47
|
-
rows: LeakRow[];
|
|
48
|
-
/** Whether this machine may raise a desktop notification. Drawn as a toggle
|
|
49
|
-
* beside the table, because the table is where somebody decides the banner
|
|
50
|
-
* was worth it or not. */
|
|
51
|
-
notify: boolean;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export async function getLeaksAction(): Promise<LeaksPayload> {
|
|
55
|
-
const findings = activeFindings(readLeakRecord());
|
|
56
|
-
const rows: LeakRow[] = findings.map((f) => {
|
|
57
|
-
// The most recent sighting: where the key is now, not where it debuted.
|
|
58
|
-
const seen = f.sightings?.[f.sightings.length - 1];
|
|
59
|
-
return {
|
|
60
|
-
id: f.id,
|
|
61
|
-
display: f.fingerprint?.display ?? "[credential]",
|
|
62
|
-
label: f.fingerprint?.label ?? "secret",
|
|
63
|
-
length: f.fingerprint?.length ?? 0,
|
|
64
|
-
attributed: f.fingerprint?.attributed === true,
|
|
65
|
-
name: f.name,
|
|
66
|
-
cli: seen?.cli ?? "unknown",
|
|
67
|
-
project: seen?.cwd ?? "unknown",
|
|
68
|
-
lastSeen: f.lastSeen,
|
|
69
|
-
firstSeen: f.firstSeen,
|
|
70
|
-
mechanism: seen?.mechanism?.summary ?? "seen in a transcript",
|
|
71
|
-
direction: seen?.mechanism?.direction ?? "result",
|
|
72
|
-
occurrences: f.occurrences,
|
|
73
|
-
sessions: new Set((f.sightings ?? []).map((s) => s.sessionId)).size,
|
|
74
|
-
};
|
|
75
|
-
});
|
|
76
|
-
rows.sort((a, b) => Date.parse(b.lastSeen) - Date.parse(a.lastSeen));
|
|
77
|
-
return { rows, notify: readConfig().audit.notify };
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Mark one finding as not-a-secret.
|
|
82
|
-
*
|
|
83
|
-
* The finding is RETAINED, not deleted — deleting it would let the next scan
|
|
84
|
-
* rediscover the same value and alert again, which is the one outcome that
|
|
85
|
-
* teaches a user the dismiss button does not work.
|
|
86
|
-
*/
|
|
87
|
-
export async function dismissLeakAction(id: string): Promise<boolean> {
|
|
88
|
-
return dismissFinding(id);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Turn the desktop banner on or off.
|
|
93
|
-
*
|
|
94
|
-
* Writes the same `audit.notify` key `failproofai config` writes and the daemon's
|
|
95
|
-
* audit child reads, so the dashboard and the CLI cannot disagree about it.
|
|
96
|
-
*/
|
|
97
|
-
export async function setLeakNotifyAction(notify: boolean): Promise<boolean> {
|
|
98
|
-
try {
|
|
99
|
-
updateConfig({ audit: { notify } });
|
|
100
|
-
return true;
|
|
101
|
-
} catch {
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* The leak report: every credential this machine found in its own transcripts.
|
|
5
|
-
*
|
|
6
|
-
* This is what replaced the persona poster and the five scored sections. The
|
|
7
|
-
* old report answered "what kind of agent user are you"; this one answers the
|
|
8
|
-
* only question a leaked key raises — what is it, where did it go, how did it
|
|
9
|
-
* get there, and what do I do now.
|
|
10
|
-
*
|
|
11
|
-
* Each row is one DISTINCT credential, not one sighting: a key pasted into
|
|
12
|
-
* forty commands is one thing to rotate, not forty. The count of exposures is a
|
|
13
|
-
* column, which is the honest way to show forty without implying forty problems.
|
|
14
|
-
*/
|
|
15
|
-
import { useCallback, useEffect, useState } from "react";
|
|
16
|
-
import {
|
|
17
|
-
dismissLeakAction,
|
|
18
|
-
getLeaksAction,
|
|
19
|
-
setLeakNotifyAction,
|
|
20
|
-
type LeakRow,
|
|
21
|
-
} from "@/app/actions/get-leaks";
|
|
22
|
-
|
|
23
|
-
/** Rendered in the reader's timezone, which is why it is not done server-side. */
|
|
24
|
-
function when(iso: string): string {
|
|
25
|
-
const t = Date.parse(iso);
|
|
26
|
-
if (!Number.isFinite(t)) return "unknown";
|
|
27
|
-
const days = Math.floor((Date.now() - t) / 86_400_000);
|
|
28
|
-
if (days <= 0) return "today";
|
|
29
|
-
if (days === 1) return "yesterday";
|
|
30
|
-
if (days < 30) return `${days}d ago`;
|
|
31
|
-
return new Date(t).toLocaleDateString(undefined, { month: "short", day: "numeric" });
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* What to actually do about this one.
|
|
36
|
-
*
|
|
37
|
-
* Two genuinely different situations, and conflating them is what makes
|
|
38
|
-
* security advice useless. A recognised prefix means a console exists and the
|
|
39
|
-
* key can be revoked there in a minute. An unattributed value — the majority,
|
|
40
|
-
* per the pattern census — has no console at all: the only move is to find
|
|
41
|
-
* where it came from, and the identifier name is usually the only clue.
|
|
42
|
-
*/
|
|
43
|
-
function advice(row: LeakRow): string {
|
|
44
|
-
if (row.attributed) return `rotate it in the ${row.label.replace(/ (API )?(key|token).*$/i, "")} console`;
|
|
45
|
-
if (row.name) return `find what reads ${row.name}, then rotate it there`;
|
|
46
|
-
return "trace it to its owner, then rotate it";
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function LeakSection() {
|
|
50
|
-
const [rows, setRows] = useState<LeakRow[] | null>(null);
|
|
51
|
-
const [notify, setNotify] = useState(true);
|
|
52
|
-
const [busy, setBusy] = useState<string | null>(null);
|
|
53
|
-
|
|
54
|
-
const load = useCallback(async () => {
|
|
55
|
-
const payload = await getLeaksAction();
|
|
56
|
-
setRows(payload.rows);
|
|
57
|
-
setNotify(payload.notify);
|
|
58
|
-
}, []);
|
|
59
|
-
|
|
60
|
-
useEffect(() => {
|
|
61
|
-
void load();
|
|
62
|
-
}, [load]);
|
|
63
|
-
|
|
64
|
-
const dismiss = useCallback(
|
|
65
|
-
async (id: string) => {
|
|
66
|
-
setBusy(id);
|
|
67
|
-
try {
|
|
68
|
-
// Optimism would be wrong here: a dismissal that did not persist means
|
|
69
|
-
// the row comes back on the next load, and the user has to guess which
|
|
70
|
-
// of the two states is real.
|
|
71
|
-
if (await dismissLeakAction(id)) setRows((prev) => prev?.filter((r) => r.id !== id) ?? null);
|
|
72
|
-
} finally {
|
|
73
|
-
setBusy(null);
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
[],
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
const toggleNotify = useCallback(async () => {
|
|
80
|
-
const next = !notify;
|
|
81
|
-
setNotify(next);
|
|
82
|
-
if (!(await setLeakNotifyAction(next))) setNotify(!next);
|
|
83
|
-
}, [notify]);
|
|
84
|
-
|
|
85
|
-
if (rows === null) return null;
|
|
86
|
-
|
|
87
|
-
return (
|
|
88
|
-
<section className="audit-sec" data-screen-label="02 Leaks">
|
|
89
|
-
<div className="audit-sec-head">
|
|
90
|
-
<span className="audit-sec-eyebrow">
|
|
91
|
-
<span className="ix">02</span>{"// credentials"}
|
|
92
|
-
</span>
|
|
93
|
-
<button
|
|
94
|
-
type="button"
|
|
95
|
-
className="cbb-toggle leak-notify"
|
|
96
|
-
data-on={notify}
|
|
97
|
-
onClick={toggleNotify}
|
|
98
|
-
aria-label={notify ? "turn desktop notifications off" : "turn desktop notifications on"}
|
|
99
|
-
title="desktop notification when a new credential is found"
|
|
100
|
-
>
|
|
101
|
-
<span className="cbb-toggle-knob" />
|
|
102
|
-
</button>
|
|
103
|
-
</div>
|
|
104
|
-
|
|
105
|
-
{rows.length === 0 ? (
|
|
106
|
-
<>
|
|
107
|
-
<h2 className="audit-sec-title">no credentials found</h2>
|
|
108
|
-
<div className="audit-sec-sub">
|
|
109
|
-
{"// nothing in this machine's transcripts matched a known key format"}
|
|
110
|
-
<br />
|
|
111
|
-
{"// or a secret-named assignment."}
|
|
112
|
-
</div>
|
|
113
|
-
</>
|
|
114
|
-
) : (
|
|
115
|
-
<>
|
|
116
|
-
<h2 className="audit-sec-title">
|
|
117
|
-
{rows.length} credential{rows.length === 1 ? "" : "s"} in your transcripts
|
|
118
|
-
</h2>
|
|
119
|
-
<div className="audit-sec-sub">
|
|
120
|
-
{"// one row per distinct key — an exposure count is not a key count."}
|
|
121
|
-
</div>
|
|
122
|
-
|
|
123
|
-
<ul className="leak-list">
|
|
124
|
-
{rows.map((row) => (
|
|
125
|
-
<li key={row.id} className="leak-row" data-attributed={row.attributed}>
|
|
126
|
-
<div className="leak-row-top">
|
|
127
|
-
<code className="leak-key">{row.display}</code>
|
|
128
|
-
<span className="leak-label">{row.label}</span>
|
|
129
|
-
{row.name ? <span className="leak-name">{row.name}</span> : null}
|
|
130
|
-
<button
|
|
131
|
-
type="button"
|
|
132
|
-
className="leak-dismiss"
|
|
133
|
-
onClick={() => void dismiss(row.id)}
|
|
134
|
-
disabled={busy === row.id}
|
|
135
|
-
>
|
|
136
|
-
not a secret
|
|
137
|
-
</button>
|
|
138
|
-
</div>
|
|
139
|
-
|
|
140
|
-
{/* The 5W1H, as one sentence rather than a grid of labelled
|
|
141
|
-
cells — a person reading a leak wants the story, and the
|
|
142
|
-
grid version made every row look like a form to fill in. */}
|
|
143
|
-
{/* The date is FIRST seen, not last. "When did this key get
|
|
144
|
-
into a transcript" is the question a leak raises; "when did
|
|
145
|
-
I last run a scan that noticed" is not. They also looked
|
|
146
|
-
identical in practice — the record keeps the 500 most
|
|
147
|
-
recently seen findings, so everything surviving the cap
|
|
148
|
-
reported the same recent `lastSeen` and every row read
|
|
149
|
-
"today", which is true and useless. */}
|
|
150
|
-
<div className="leak-how">
|
|
151
|
-
<span className="leak-verb">{row.mechanism}</span>
|
|
152
|
-
{" in "}
|
|
153
|
-
<span className="leak-where">{row.project}</span>
|
|
154
|
-
{" via "}
|
|
155
|
-
<span className="leak-who">{row.cli}</span>
|
|
156
|
-
{" · leaked "}
|
|
157
|
-
<span className="leak-when">{when(row.firstSeen)}</span>
|
|
158
|
-
{row.firstSeen !== row.lastSeen ? (
|
|
159
|
-
<>
|
|
160
|
-
{" · last seen "}
|
|
161
|
-
<span className="leak-when">{when(row.lastSeen)}</span>
|
|
162
|
-
</>
|
|
163
|
-
) : null}
|
|
164
|
-
</div>
|
|
165
|
-
|
|
166
|
-
<div className="leak-foot">
|
|
167
|
-
<span className="leak-meta">
|
|
168
|
-
{row.occurrences} exposure{row.occurrences === 1 ? "" : "s"}
|
|
169
|
-
{row.sessions > 1 ? ` · ${row.sessions} sessions` : ""}
|
|
170
|
-
{" · "}
|
|
171
|
-
{row.length} chars
|
|
172
|
-
</span>
|
|
173
|
-
{/* The direction is the difference between a leak a gate can
|
|
174
|
-
stop next time and one it cannot. An input travelled OUT
|
|
175
|
-
of the machine with the agent's request; a result came
|
|
176
|
-
back INTO the transcript from a file or a command, where
|
|
177
|
-
no PreToolUse hook can intercept it. */}
|
|
178
|
-
<span className="leak-dir">
|
|
179
|
-
{row.direction === "input"
|
|
180
|
-
? "sent by the agent — blockable at PreToolUse"
|
|
181
|
-
: "returned to the agent — already in the transcript"}
|
|
182
|
-
</span>
|
|
183
|
-
<span className="leak-fix">{advice(row)}</span>
|
|
184
|
-
</div>
|
|
185
|
-
</li>
|
|
186
|
-
))}
|
|
187
|
-
</ul>
|
|
188
|
-
</>
|
|
189
|
-
)}
|
|
190
|
-
</section>
|
|
191
|
-
);
|
|
192
|
-
}
|