failproofai 1.0.3 → 1.0.4-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +5 -5
- package/.next/standalone/.next/prerender-manifest.json +5 -5
- 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.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.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 +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/invite/route_client-reference-manifest.js +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/run/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route_client-reference-manifest.js +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-request/route_client-reference-manifest.js +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/login-verify/route_client-reference-manifest.js +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/logout/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route_client-reference-manifest.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/api/download/[project]/[session]/route_client-reference-manifest.js +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 +37 -4
- 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 +15 -15
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- 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.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.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.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.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.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 -4
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__0-9ti7z._.js → [externals]__1lh7m5d._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__0u3wrvq._.js → [externals]__1rqkg_y._.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]__1adacul._.js +1 -1
- package/.next/standalone/.next/server/chunks/{_1-akg6w._.js → _08w6xzm._.js} +2 -2
- 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/_1ek68ln._.js +4 -4
- package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +1 -1
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +2 -2
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +2 -2
- 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]__013jr2b._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0rgu2r3._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_0-oij9d._.js +23 -0
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0l2hi_d._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0oq1dh6._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1es2j7i._.js +47 -13
- package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +1 -1
- 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 +1 -69
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_rerun-button_tsx_0blihgw._.js +3 -0
- 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_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +2 -2
- 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 +54 -21
- package/.next/standalone/.next/static/chunks/010bv1w6j171t.js +1 -0
- package/.next/standalone/.next/static/chunks/04r6ch8uf_n8m.js +1 -0
- package/.next/standalone/.next/static/chunks/{29-iqnp9v_axt.css → 0o-hh5_turzlz.css} +1 -1
- package/.next/standalone/.next/static/chunks/{40nr6ehj4p3cw.css → 0qmsunv04x4na.css} +1 -1
- package/.next/standalone/.next/static/chunks/{0ollg-nnu3t3e.js → 0wz8yftk18ts2.js} +1 -1
- package/.next/standalone/.next/static/chunks/0zebh1n9jkfbt.js +1 -0
- package/.next/standalone/.next/static/chunks/1eyl7fzkmnkf_.js +1 -0
- package/.next/standalone/.next/static/chunks/1pb1oztsbwcss.js +1 -0
- package/.next/standalone/.next/static/chunks/{1-_zkmnm-jwei.js → 2aquitk72k2op.js} +1 -1
- package/.next/standalone/.next/static/chunks/2bi_1y0a_smt7.js +6 -0
- package/.next/standalone/.next/static/chunks/{42yfkhekx440s.js → 2ej3b8gk5ittu.js} +1 -1
- package/.next/standalone/.next/static/chunks/{354-n7p7labpn.js → 2zafgs90r_leq.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2xm5mj1d21b4n.js → 32spub4wqjem-.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1a6u7l3giejch.js → 3m4upvybtrexd.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2k9f4tyv04809.css → 3uyhmm01g64k4.css} +1 -0
- package/.next/standalone/.next/static/chunks/{turbopack-2x-o099q64i8h.js → turbopack-3s3c-u_u_l0u9.js} +1 -1
- package/.next/standalone/.opencode/plugins/failproofai.mjs +51 -27
- package/.next/standalone/app/actions/get-leaks.ts +100 -0
- package/.next/standalone/app/audit/_components/audit-dashboard.tsx +112 -33
- package/.next/standalone/app/audit/_components/audit-poster.tsx +29 -10
- package/.next/standalone/app/audit/_components/come-back-better-section.tsx +6 -1
- package/.next/standalone/app/audit/_components/empty-state.tsx +4 -1
- package/.next/standalone/app/audit/_components/how-to-improve-section.tsx +19 -5
- package/.next/standalone/app/audit/_components/leak-section.tsx +179 -0
- package/.next/standalone/app/audit/_components/share-templates.ts +110 -44
- package/.next/standalone/app/audit/audit-styles.css +114 -1
- package/.next/standalone/fp-cloud-cli/CHANGELOG.md +26 -3
- package/.next/standalone/fp-cloud-cli/fp_cli/_click_compat.py +31 -8
- package/.next/standalone/fp-cloud-cli/fp_cli/permissions.py +1 -0
- package/.next/standalone/fp-cloud-cli/pyproject.toml +9 -2
- package/.next/standalone/fp-cloud-cli/tests/test_click_compat.py +54 -0
- package/.next/standalone/fp-cloud-cli/uv.lock +9 -12
- package/.next/standalone/lib/auth/api-server-client.ts +28 -0
- package/.next/standalone/lib/claude-sessions.ts +101 -27
- package/.next/standalone/node_modules/@next/env/package.json +1 -1
- package/.next/standalone/node_modules/next/dist/build/swc/index.js +1 -1
- package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo-experimental.runtime.prod.js +1 -1
- package/.next/standalone/node_modules/next/dist/compiled/next-server/app-page-turbo.runtime.prod.js +1 -1
- package/.next/standalone/node_modules/next/dist/compiled/next-server/app-route-turbo.runtime.prod.js +1 -1
- package/.next/standalone/node_modules/next/dist/compiled/next-server/pages-turbo.runtime.prod.js +5 -5
- package/.next/standalone/node_modules/next/dist/experimental/testmode/fetch.js +10 -2
- package/.next/standalone/node_modules/next/dist/lib/patch-incorrect-lockfile.js +3 -3
- package/.next/standalone/node_modules/next/dist/lib/typescript/runTypeScriptCli.js +2 -2
- package/.next/standalone/node_modules/next/dist/lib/verify-typescript-setup.js +3 -1
- package/.next/standalone/node_modules/next/dist/server/config.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-turbopack.js +2 -2
- package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-webpack.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/lib/app-info-log.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/lib/incremental-cache/file-system-cache.js +17 -4
- package/.next/standalone/node_modules/next/dist/server/lib/start-server.js +1 -1
- package/.next/standalone/node_modules/next/dist/shared/lib/router/utils/escape-path-delimiters.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/anonymous-meta.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/events/swc-load-failure.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/events/version.js +2 -2
- package/.next/standalone/node_modules/next/package.json +11 -11
- package/.next/standalone/package.json +14 -13
- package/.next/standalone/sdk/python/CHANGELOG.md +155 -0
- package/.next/standalone/sdk/python/README.md +8 -0
- package/.next/standalone/sdk/python/examples/evaluator_worker.py +121 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_version.py +1 -1
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/__init__.py +101 -0
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/__main__.py +49 -0
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/_sandbox_runner.py +63 -0
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/authoring.py +404 -0
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/client.py +299 -0
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/protocol.py +754 -0
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/runtime.py +936 -0
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/source.py +684 -0
- package/.next/standalone/sdk/python/tests/fixtures/evaluator_v2/README.md +28 -0
- package/.next/standalone/sdk/python/tests/fixtures/evaluator_v2/contract.json +252 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_authoring.py +124 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_client.py +253 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_example.py +35 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_http_e2e.py +636 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_main.py +47 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_protocol.py +246 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_review_fixes.py +164 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_runtime.py +1103 -0
- package/.next/standalone/sdk/python/tests/test_evaluator_source.py +430 -0
- package/.next/standalone/sdk/python/tests/test_zero_dependencies.py +17 -0
- package/.next/standalone/sdk/python/uv.lock +29 -29
- package/.next/standalone/server.js +1 -1
- package/README.md +13 -58
- package/dist/cli.mjs +5368 -8756
- package/dist/worker.mjs +539 -312
- package/lib/auth/api-server-client.ts +28 -0
- package/lib/claude-sessions.ts +101 -27
- package/package.json +14 -13
- package/pi-extension/index.ts +42 -4
- package/scripts/changelog-open.py +115 -0
- package/src/audit/cli.ts +134 -0
- package/src/audit/desktop-notify.ts +420 -0
- package/src/audit/harm-report.ts +101 -0
- package/src/audit/index.ts +141 -13
- package/src/audit/leak-fingerprint.ts +200 -0
- package/src/audit/leak-notice.ts +161 -0
- package/src/audit/leak-record.ts +232 -0
- package/src/audit/leak-scan.ts +292 -0
- package/src/audit/leak-store.ts +217 -0
- package/src/audit/macos-notifier.ts +310 -0
- package/src/audit/redact-example.ts +266 -14
- package/src/audit/report-harm.ts +12 -1
- package/src/audit/report.ts +6 -3
- package/src/audit/schedule-cli.ts +37 -0
- package/src/audit/scoring.ts +49 -0
- package/src/audit/types.ts +41 -0
- package/src/hooks/builtin-policies.ts +67 -2
- package/src/hooks/configure-wizard.ts +20 -0
- package/src/hooks/fp-config.ts +71 -11
- package/src/hooks/fp-home.ts +27 -0
- package/src/hooks/handler.ts +59 -1
- package/src/hooks/integrations.ts +47 -13
- package/src/hooks/notice.ts +155 -0
- package/src/hooks/uninstall-cli.ts +15 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1b5jzxr._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0j6gd_k._.js +0 -23
- package/.next/standalone/.next/server/chunks/ssr/_0v8eebd._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_1ylae7o._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_1ao30b1.js +0 -3
- package/.next/standalone/.next/static/chunks/03slu0c8_71y7.js +0 -1
- package/.next/standalone/.next/static/chunks/0nm11ysn0153h.js +0 -1
- package/.next/standalone/.next/static/chunks/0u64yey446k4f.css +0 -1
- package/.next/standalone/.next/static/chunks/1r9676mkvihto.js +0 -1
- package/.next/standalone/.next/static/chunks/30pct5s734jua.js +0 -6
- package/.next/standalone/.next/static/chunks/3pibxnbhjza7n.js +0 -69
- package/.next/standalone/.next/static/chunks/3qfoeqhy5l10x.js +0 -1
- package/.next/standalone/.next/static/chunks/3zkg2s2vzxc3d.js +0 -1
- package/.next/standalone/.next/static/chunks/41b5ptaocvi_l.js +0 -1
- /package/.next/standalone/.next/static/{JBYgkXkA1PS48m9P_hKNw → aKNv4Kmv98Xpwns0Xa-C-}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{JBYgkXkA1PS48m9P_hKNw → aKNv4Kmv98Xpwns0Xa-C-}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{JBYgkXkA1PS48m9P_hKNw → aKNv4Kmv98Xpwns0Xa-C-}/_ssgManifest.js +0 -0
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Top-level client wrapper for /audit.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* The old five-section personality report is SWITCHED OFF and being rebuilt
|
|
7
|
+
* bottom-up around leak detection. What it used to compose:
|
|
8
8
|
*
|
|
9
9
|
* 01 AuditPoster — single-screen shareable poster
|
|
10
10
|
* 02 StrengthsSection — what it's great at
|
|
@@ -12,23 +12,36 @@
|
|
|
12
12
|
* 04 HowToImproveSection — install / configure
|
|
13
13
|
* 05 ComeBackBetterSection — spread the audit (invite)
|
|
14
14
|
*
|
|
15
|
+
* Every one of those components, and the score / persona / strengths / findings
|
|
16
|
+
* modules behind them, is intact and still unit-tested — only the renders and
|
|
17
|
+
* the imports here are commented out, so restoring any of it is deleting a pair
|
|
18
|
+
* of comment markers. See the header of `src/audit/scoring.ts` for why.
|
|
19
|
+
*
|
|
20
|
+
* What this page renders now is `AuditReportPlaceholder`: the scan's own
|
|
21
|
+
* numbers. The scan, the 12-CLI transcript reader and the emailed digest all
|
|
22
|
+
* still run.
|
|
23
|
+
*
|
|
15
24
|
* Empty / running states fall back to EmptyState and RunProgress.
|
|
16
25
|
*/
|
|
17
26
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
18
27
|
import { getAuditResultAction } from "@/app/actions/get-audit-result";
|
|
19
28
|
import type { AuditResult, RunAuditOptions } from "@/src/audit/types";
|
|
20
|
-
import { classifyAgent } from "@/src/audit/archetypes";
|
|
21
|
-
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
29
|
+
// import { classifyAgent } from "@/src/audit/archetypes";
|
|
30
|
+
// Score switched off — see the header of `src/audit/scoring.ts`.
|
|
31
|
+
// import { deriveScore, gradeFor, projectedScore } from "@/src/audit/scoring";
|
|
32
|
+
// import { deriveStrengths } from "@/src/audit/strengths";
|
|
33
|
+
// import { deriveFindings } from "@/src/audit/findings";
|
|
24
34
|
import { usePostHog } from "@/contexts/PostHogContext";
|
|
25
35
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
36
|
+
// The old report's five sections — switched off, not deleted. Each component
|
|
37
|
+
// file and its tests are untouched; only this render is commented out.
|
|
38
|
+
// import { AuditPoster } from "./audit-poster";
|
|
39
|
+
// import { StrengthsSection } from "./strengths-section";
|
|
40
|
+
// import { QuirksSection } from "./quirks-section";
|
|
41
|
+
// import { HowToImproveSection } from "./how-to-improve-section";
|
|
42
|
+
// import { ComeBackBetterSection } from "./come-back-better-section";
|
|
31
43
|
import { ReportFooter } from "./report-footer";
|
|
44
|
+
import { LeakSection } from "./leak-section";
|
|
32
45
|
import { EmptyState } from "./empty-state";
|
|
33
46
|
import { RunProgress } from "./run-progress";
|
|
34
47
|
import { AuditProgressStrip, type RerunStatus } from "./audit-progress-strip";
|
|
@@ -59,6 +72,9 @@ interface Props {
|
|
|
59
72
|
totalCatalogSize: number;
|
|
60
73
|
}
|
|
61
74
|
|
|
75
|
+
/* Switched off with the persona layer — this named the project that seeded the
|
|
76
|
+
archetype classifier and the leaderboard row. Restore alongside
|
|
77
|
+
`classifyAgent` in this file.
|
|
62
78
|
function inferProjectName(result: AuditResult, override?: string): string {
|
|
63
79
|
if (override && override.trim()) return override;
|
|
64
80
|
// Pick the cwd that appears in the most examples — proxy for "your
|
|
@@ -81,6 +97,7 @@ function inferProjectName(result: AuditResult, override?: string): string {
|
|
|
81
97
|
if (segs.length >= 2) return `${segs[segs.length - 2]} / ${segs[segs.length - 1]}`;
|
|
82
98
|
return segs[segs.length - 1] ?? "your agent";
|
|
83
99
|
}
|
|
100
|
+
*/
|
|
84
101
|
|
|
85
102
|
export function AuditDashboard({ initial, projectFromUrl, totalCatalogSize }: Props) {
|
|
86
103
|
const [cache, setCache] = useState<Initial>(initial);
|
|
@@ -279,16 +296,22 @@ function MainReport({
|
|
|
279
296
|
onDismissRerun,
|
|
280
297
|
}: MainReportProps) {
|
|
281
298
|
const { capture } = usePostHog();
|
|
282
|
-
|
|
299
|
+
// Only fed the persona classifier, which is switched off with it.
|
|
300
|
+
// const project = useMemo(() => inferProjectName(result, projectFromUrl), [result, projectFromUrl]);
|
|
283
301
|
// Seed classification with the project name so the behaviour fingerprint
|
|
284
302
|
// (used for tie-breaks + copy variants) is stable per project.
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
const
|
|
303
|
+
// Persona classification is switched off with the rest of the old report. Its
|
|
304
|
+
// last live use was two telemetry properties describing a persona the product
|
|
305
|
+
// no longer shows anyone.
|
|
306
|
+
// const classification = useMemo(() => classifyAgent(result, project), [result, project]);
|
|
307
|
+
// Score switched off — see the header of `src/audit/scoring.ts`. The four
|
|
308
|
+
// functions are intact and still unit-tested; nothing calls them.
|
|
309
|
+
// const score = useMemo(() => deriveScore(result), [result]);
|
|
310
|
+
// const projected = useMemo(() => projectedScore(result, score), [result, score]);
|
|
311
|
+
// const grade = gradeFor(score);
|
|
312
|
+
// const projectedGrade = gradeFor(projected);
|
|
313
|
+
// const strengths = useMemo(() => deriveStrengths(result), [result]);
|
|
314
|
+
// const findings = useMemo(() => deriveFindings(result), [result]);
|
|
292
315
|
|
|
293
316
|
// One pass over result.results: detectors triggered + missing prescribed
|
|
294
317
|
// policies. Both feed PostHog instrumentation; `missing` also feeds the
|
|
@@ -310,10 +333,13 @@ function MainReport({
|
|
|
310
333
|
if (dashboardViewedRef.current) return;
|
|
311
334
|
dashboardViewedRef.current = true;
|
|
312
335
|
capture("audit_dashboard_viewed", {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
336
|
+
// Score switched off — see `src/audit/scoring.ts`. `missing` is the
|
|
337
|
+
// prescription's denominator and is unaffected, so the copy→install
|
|
338
|
+
// funnel keeps both of its ends.
|
|
339
|
+
// score,
|
|
340
|
+
// grade,
|
|
341
|
+
// archetype: classification.archetype,
|
|
342
|
+
// secondary: classification.secondary ?? null,
|
|
317
343
|
missing,
|
|
318
344
|
transcripts_scanned: result.transcripts.scanned,
|
|
319
345
|
results_count: result.results.length,
|
|
@@ -321,10 +347,6 @@ function MainReport({
|
|
|
321
347
|
});
|
|
322
348
|
}, [
|
|
323
349
|
capture,
|
|
324
|
-
score,
|
|
325
|
-
grade,
|
|
326
|
-
classification.archetype,
|
|
327
|
-
classification.secondary,
|
|
328
350
|
missing,
|
|
329
351
|
result.transcripts.scanned,
|
|
330
352
|
result.results.length,
|
|
@@ -332,19 +354,41 @@ function MainReport({
|
|
|
332
354
|
]);
|
|
333
355
|
|
|
334
356
|
/** Poster ref — captured to PNG by the poster's share buttons. */
|
|
335
|
-
const posterRef = useRef<HTMLDivElement>(null);
|
|
357
|
+
// const posterRef = useRef<HTMLDivElement>(null);
|
|
336
358
|
|
|
337
359
|
return (
|
|
338
360
|
<div className="app">
|
|
339
361
|
<AuditProgressStrip status={rerunStatus} onDismiss={onDismissRerun} />
|
|
340
362
|
<div className="app-shell">
|
|
341
363
|
<div className="report">
|
|
364
|
+
<AuditReportPlaceholder
|
|
365
|
+
transcripts={result.transcripts.scanned}
|
|
366
|
+
events={result.eventsScanned ?? 0}
|
|
367
|
+
projects={result.projectsScanned.length}
|
|
368
|
+
/>
|
|
369
|
+
{/* The rebuilt report. It reads the leak record rather than this
|
|
370
|
+
scan's result, deliberately: a credential found last month and
|
|
371
|
+
not touched by today's transcripts is still a credential to
|
|
372
|
+
rotate, and a report that showed only the current scan's findings
|
|
373
|
+
would go quiet on exactly those. */}
|
|
374
|
+
<LeakSection />
|
|
375
|
+
{/* The whole old report is switched off — the persona poster, the
|
|
376
|
+
strengths and quirks sections, the punch-list with its install-all
|
|
377
|
+
funnel, and the invite section. It is being rebuilt bottom-up
|
|
378
|
+
around leak detection; see the header of `src/audit/scoring.ts`
|
|
379
|
+
for the reasoning and `~/Desktop/failproofai-leak-detection-
|
|
380
|
+
verdict-2026-09-07.md` for the measurements behind it.
|
|
381
|
+
|
|
382
|
+
Every component below is intact and still unit-tested — only the
|
|
383
|
+
render is commented — so restoring any one of them is deleting a
|
|
384
|
+
pair of comment markers.
|
|
385
|
+
|
|
342
386
|
<AuditPoster
|
|
343
387
|
ref={posterRef}
|
|
344
388
|
archetypeKey={classification.archetype}
|
|
345
389
|
seed={classification.variantSeed}
|
|
346
|
-
score={score}
|
|
347
|
-
grade={grade}
|
|
390
|
+
// score={score}
|
|
391
|
+
// grade={grade}
|
|
348
392
|
missing={missing}
|
|
349
393
|
auditedAt={cachedAt ?? new Date().toISOString()}
|
|
350
394
|
/>
|
|
@@ -352,10 +396,11 @@ function MainReport({
|
|
|
352
396
|
<QuirksSection findings={findings} />
|
|
353
397
|
<HowToImproveSection
|
|
354
398
|
result={result}
|
|
355
|
-
|
|
356
|
-
|
|
399
|
+
// projected={projected}
|
|
400
|
+
// projectedGrade={projectedGrade}
|
|
357
401
|
/>
|
|
358
|
-
<ComeBackBetterSection
|
|
402
|
+
<ComeBackBetterSection />
|
|
403
|
+
*/}
|
|
359
404
|
</div>
|
|
360
405
|
<ReportFooter cachedAt={cachedAt} />
|
|
361
406
|
</div>
|
|
@@ -363,6 +408,40 @@ function MainReport({
|
|
|
363
408
|
);
|
|
364
409
|
}
|
|
365
410
|
|
|
411
|
+
/**
|
|
412
|
+
* What `/audit` shows while the report is being rebuilt.
|
|
413
|
+
*
|
|
414
|
+
* Deliberately states the scan's own numbers rather than nothing at all: the
|
|
415
|
+
* scan still runs, still walks every transcript across all 12 CLIs, and still
|
|
416
|
+
* feeds the emailed digest. Saying so is the difference between "this page is
|
|
417
|
+
* under construction" and "this product is broken".
|
|
418
|
+
*/
|
|
419
|
+
function AuditReportPlaceholder(
|
|
420
|
+
{ transcripts, events, projects }: { transcripts: number; events: number; projects: number },
|
|
421
|
+
) {
|
|
422
|
+
const n = (x: number) => x.toLocaleString();
|
|
423
|
+
return (
|
|
424
|
+
<section className="audit-sec" data-screen-label="01 Scan">
|
|
425
|
+
<div className="audit-sec-head">
|
|
426
|
+
<span className="audit-sec-eyebrow">
|
|
427
|
+
<span className="ix">01</span>{"// scan"}
|
|
428
|
+
</span>
|
|
429
|
+
</div>
|
|
430
|
+
<h2 className="audit-sec-title">scan complete</h2>
|
|
431
|
+
<div className="audit-sec-sub">
|
|
432
|
+
{n(events)} tool call{events === 1 ? "" : "s"} across {n(transcripts)}{" "}
|
|
433
|
+
transcript{transcripts === 1 ? "" : "s"}
|
|
434
|
+
{projects > 0 ? ` · ${n(projects)} project${projects === 1 ? "" : "s"}` : ""}
|
|
435
|
+
</div>
|
|
436
|
+
<div className="audit-sec-sub">
|
|
437
|
+
{"// the report is being rebuilt around leak detection. the scan, the"}
|
|
438
|
+
<br />
|
|
439
|
+
{"// 12-CLI transcript reader and the emailed digest are unaffected."}
|
|
440
|
+
</div>
|
|
441
|
+
</section>
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
|
|
366
445
|
interface ShellEmptyProps {
|
|
367
446
|
running: boolean;
|
|
368
447
|
mode?: "no-cache" | "zero-sessions";
|
|
@@ -24,7 +24,12 @@
|
|
|
24
24
|
import React, { forwardRef, useMemo, useState } from "react";
|
|
25
25
|
import { pickArchetypeVariant, type ArchetypeKey } from "@/src/audit/archetypes";
|
|
26
26
|
import { type Grade } from "@/src/audit/scoring";
|
|
27
|
-
|
|
27
|
+
// Archetype rarity switched off — the percentages are eight hardcoded integers
|
|
28
|
+
// ("Seeded with snapshot values; swap for live aggregates once that pipeline
|
|
29
|
+
// lands"), and this is the ONLY surface that rendered them: baked by
|
|
30
|
+
// html-to-image into the PNG people post publicly. A fabricated population
|
|
31
|
+
// statistic on a shared card is a claim we cannot support.
|
|
32
|
+
// import { getArchetypeRarityPct } from "@/src/audit/social-proof";
|
|
28
33
|
import { copyOrDownloadCard, downloadCard, shareCardNative, shareCardToastMessage } from "@/lib/share-card";
|
|
29
34
|
import { toast } from "@/app/components/toast";
|
|
30
35
|
import { usePostHog } from "@/contexts/PostHogContext";
|
|
@@ -45,8 +50,11 @@ interface Props {
|
|
|
45
50
|
archetypeKey: ArchetypeKey;
|
|
46
51
|
/** Stable seed for variant selection (project name is the natural fit). */
|
|
47
52
|
seed: string;
|
|
48
|
-
score
|
|
49
|
-
|
|
53
|
+
/** The score is switched off (see `src/audit/scoring.ts`). Kept as optional
|
|
54
|
+
* props rather than removed, so restoring is un-commenting rather than
|
|
55
|
+
* re-threading: the dashboard simply stops passing them. */
|
|
56
|
+
score?: number;
|
|
57
|
+
grade?: Grade;
|
|
50
58
|
/** Count of unenabled prescribed policies — passed to the share-text
|
|
51
59
|
* templates, not rendered on the poster itself. */
|
|
52
60
|
missing: number;
|
|
@@ -63,7 +71,7 @@ export const AuditPoster = forwardRef<HTMLDivElement, Props>(function AuditPoste
|
|
|
63
71
|
() => pickArchetypeVariant(archetypeKey, seed),
|
|
64
72
|
[archetypeKey, seed],
|
|
65
73
|
);
|
|
66
|
-
const rarityPct = getArchetypeRarityPct(archetypeKey);
|
|
74
|
+
// const rarityPct = getArchetypeRarityPct(archetypeKey);
|
|
67
75
|
const indexLabel = String(archetype.index).padStart(2, "0");
|
|
68
76
|
const auditedDate = useMemo(() => formatAuditedDate(auditedAt), [auditedAt]);
|
|
69
77
|
|
|
@@ -134,8 +142,11 @@ export const AuditPoster = forwardRef<HTMLDivElement, Props>(function AuditPoste
|
|
|
134
142
|
}
|
|
135
143
|
};
|
|
136
144
|
|
|
145
|
+
// Score switched off — the filename is keyed on the archetype instead, which
|
|
146
|
+
// is what the card now actually shows. Original:
|
|
147
|
+
// `failproofai-${channel}-${grade.toLowerCase()}-${score}.png`
|
|
137
148
|
const filenameFor = (channel: "x" | "linkedin" | "download") =>
|
|
138
|
-
`failproofai-${channel}-${
|
|
149
|
+
`failproofai-${channel}-${archetypeKey}.png`;
|
|
139
150
|
|
|
140
151
|
const handleShare = async (channel: "x" | "linkedin" | "download") => {
|
|
141
152
|
if (busy) return;
|
|
@@ -143,8 +154,11 @@ export const AuditPoster = forwardRef<HTMLDivElement, Props>(function AuditPoste
|
|
|
143
154
|
capture("audit_card_share_clicked", {
|
|
144
155
|
channel,
|
|
145
156
|
source: "poster",
|
|
146
|
-
|
|
147
|
-
|
|
157
|
+
// Score switched off — see `src/audit/scoring.ts`. Note for whoever reads
|
|
158
|
+
// the funnel: these two properties stop appearing from this release, so a
|
|
159
|
+
// dashboard keyed on them breaks rather than reading zero.
|
|
160
|
+
// score,
|
|
161
|
+
// grade,
|
|
148
162
|
missing_policies: missing,
|
|
149
163
|
});
|
|
150
164
|
try {
|
|
@@ -174,10 +188,10 @@ export const AuditPoster = forwardRef<HTMLDivElement, Props>(function AuditPoste
|
|
|
174
188
|
}
|
|
175
189
|
|
|
176
190
|
const shareCtx: ShareCtx = {
|
|
177
|
-
score,
|
|
178
191
|
arch: archetype.name.toLowerCase(),
|
|
179
|
-
grade,
|
|
180
192
|
missing,
|
|
193
|
+
// score,
|
|
194
|
+
// grade,
|
|
181
195
|
};
|
|
182
196
|
const shareText = channel === "x"
|
|
183
197
|
? pickTemplate(X_TEMPLATES, seed, shareCtx)
|
|
@@ -248,6 +262,7 @@ export const AuditPoster = forwardRef<HTMLDivElement, Props>(function AuditPoste
|
|
|
248
262
|
</React.Fragment>
|
|
249
263
|
))}
|
|
250
264
|
</div>
|
|
265
|
+
{/* Rarity switched off — see the import above.
|
|
251
266
|
{typeof rarityPct === "number" && (
|
|
252
267
|
<div className="persona-rarity">
|
|
253
268
|
<span className="lbl">{"// only"}</span>{" "}
|
|
@@ -255,13 +270,17 @@ export const AuditPoster = forwardRef<HTMLDivElement, Props>(function AuditPoste
|
|
|
255
270
|
<span className="lbl">of agents are this archetype</span>
|
|
256
271
|
</div>
|
|
257
272
|
)}
|
|
273
|
+
*/}
|
|
258
274
|
</div>
|
|
259
275
|
|
|
260
|
-
{/* Score block — heroic number, centered in the card
|
|
276
|
+
{/* Score block — heroic number, centered in the card. Switched off;
|
|
277
|
+
see `src/audit/scoring.ts` for why. The archetype is the card's
|
|
278
|
+
headline now.
|
|
261
279
|
<div className="poster-score">
|
|
262
280
|
<span className="score-n">{score}</span>
|
|
263
281
|
<span className="score-of">/100</span>
|
|
264
282
|
</div>
|
|
283
|
+
*/}
|
|
265
284
|
</div>
|
|
266
285
|
|
|
267
286
|
<footer className="poster-foot">
|
|
@@ -21,7 +21,12 @@ import { AuthDialog, type AuthedUser } from "./auth-dialog";
|
|
|
21
21
|
import { InviteDialog } from "./invite-dialog";
|
|
22
22
|
|
|
23
23
|
interface Props {
|
|
24
|
-
/** Current audit score (0–100), forwarded into the invite email body.
|
|
24
|
+
/** Current audit score (0–100), forwarded into the invite email body.
|
|
25
|
+
*
|
|
26
|
+
* Always undefined while the score is switched off (see
|
|
27
|
+
* `src/audit/scoring.ts`) — the dashboard no longer passes it. Nothing
|
|
28
|
+
* breaks: `/api/audit/invite` already treats it as optional and simply
|
|
29
|
+
* leaves it out of the email body when it is not a finite number. */
|
|
25
30
|
score?: number;
|
|
26
31
|
}
|
|
27
32
|
|
|
@@ -74,7 +74,10 @@ export function EmptyState({ mode, running, onStarted, onCompleted }: Props) {
|
|
|
74
74
|
<p className="empty-sub">
|
|
75
75
|
we'll walk every transcript across your installed CLIs — Claude Code,
|
|
76
76
|
Codex, Copilot, Cursor, OpenCode, Pi — and count every wasteful
|
|
77
|
-
|
|
77
|
+
{/* Was "a tier, a score, and a punch-list" — the tier was never
|
|
78
|
+
rendered and the score is switched off, so this promised two
|
|
79
|
+
things the page did not show. */}
|
|
80
|
+
or risky action. you'll get your agent's archetype and a punch-list.
|
|
78
81
|
</p>
|
|
79
82
|
|
|
80
83
|
<div className="empty-actions">
|
|
@@ -12,13 +12,18 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { useMemo, useState } from "react";
|
|
14
14
|
import type { AuditResult } from "@/src/audit/types";
|
|
15
|
-
|
|
15
|
+
// Score switched off — see `src/audit/scoring.ts`.
|
|
16
|
+
// import { type Grade, tierName } from "@/src/audit/scoring";
|
|
17
|
+
import type { Grade } from "@/src/audit/scoring";
|
|
16
18
|
import { usePostHog } from "@/contexts/PostHogContext";
|
|
17
19
|
|
|
18
20
|
interface Props {
|
|
19
21
|
result: AuditResult;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
/** The projection is switched off — see `src/audit/scoring.ts`. Optional
|
|
23
|
+
* rather than removed so restoring it is un-commenting the two lines in
|
|
24
|
+
* `audit-dashboard.tsx` and the sub-heading below. */
|
|
25
|
+
projected?: number;
|
|
26
|
+
projectedGrade?: Grade;
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
const DETECTOR_TO_PRIMARY_POLICY: Record<string, string> = {
|
|
@@ -99,7 +104,7 @@ function bulkInstall(fixes: FixRow[]): string {
|
|
|
99
104
|
return `failproofai policies --install ${fixes.map((f) => f.name).join(" ")}`;
|
|
100
105
|
}
|
|
101
106
|
|
|
102
|
-
export function HowToImproveSection({ result
|
|
107
|
+
export function HowToImproveSection({ result }: Props) {
|
|
103
108
|
const { capture } = usePostHog();
|
|
104
109
|
const fixes = useMemo(() => buildFixes(result), [result]);
|
|
105
110
|
const installAllCmd = useMemo(() => bulkInstall(fixes), [fixes]);
|
|
@@ -136,9 +141,18 @@ export function HowToImproveSection({ result, projected, projectedGrade }: Props
|
|
|
136
141
|
</button>
|
|
137
142
|
</div>
|
|
138
143
|
<h2 className="audit-sec-title">install or configure</h2>
|
|
139
|
-
|
|
144
|
+
{/* The projected-score clause is switched off. It was also the one place
|
|
145
|
+
the product promised a number it could not deliver: `deriveScore`
|
|
146
|
+
never read `enabledInConfig`, so taking this prescription and
|
|
147
|
+
re-running returned the SAME score with the projection gone. What
|
|
148
|
+
replaces it is the thing that was always true — one command, enforced
|
|
149
|
+
on every tool call. Original:
|
|
140
150
|
enable all {fixes.length === 1 ? "one" : fixes.length} → projected{" "}
|
|
141
151
|
<strong>{projected}</strong> · {tierName(projectedGrade).toLowerCase()}
|
|
152
|
+
*/}
|
|
153
|
+
<div className="audit-sec-sub">
|
|
154
|
+
enable all {fixes.length === 1 ? "one" : fixes.length} → one command,
|
|
155
|
+
enforced on every tool call
|
|
142
156
|
</div>
|
|
143
157
|
|
|
144
158
|
<div className="fix-list">
|
|
@@ -0,0 +1,179 @@
|
|
|
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
|
+
<div className="leak-how">
|
|
144
|
+
<span className="leak-verb">{row.mechanism}</span>
|
|
145
|
+
{" in "}
|
|
146
|
+
<span className="leak-where">{row.project}</span>
|
|
147
|
+
{" via "}
|
|
148
|
+
<span className="leak-who">{row.cli}</span>
|
|
149
|
+
{" · "}
|
|
150
|
+
<span className="leak-when">{when(row.lastSeen)}</span>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<div className="leak-foot">
|
|
154
|
+
<span className="leak-meta">
|
|
155
|
+
{row.occurrences} exposure{row.occurrences === 1 ? "" : "s"}
|
|
156
|
+
{row.sessions > 1 ? ` · ${row.sessions} sessions` : ""}
|
|
157
|
+
{" · "}
|
|
158
|
+
{row.length} chars
|
|
159
|
+
</span>
|
|
160
|
+
{/* The direction is the difference between a leak a gate can
|
|
161
|
+
stop next time and one it cannot. An input travelled OUT
|
|
162
|
+
of the machine with the agent's request; a result came
|
|
163
|
+
back INTO the transcript from a file or a command, where
|
|
164
|
+
no PreToolUse hook can intercept it. */}
|
|
165
|
+
<span className="leak-dir">
|
|
166
|
+
{row.direction === "input"
|
|
167
|
+
? "sent by the agent — blockable at PreToolUse"
|
|
168
|
+
: "returned to the agent — already in the transcript"}
|
|
169
|
+
</span>
|
|
170
|
+
<span className="leak-fix">{advice(row)}</span>
|
|
171
|
+
</div>
|
|
172
|
+
</li>
|
|
173
|
+
))}
|
|
174
|
+
</ul>
|
|
175
|
+
</>
|
|
176
|
+
)}
|
|
177
|
+
</section>
|
|
178
|
+
);
|
|
179
|
+
}
|