failproofai 1.0.4-beta.2 → 1.0.4-beta.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 +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js +1 -1
- 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/server-reference-manifest.json +5 -5
- package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -4
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__1lh7m5d._.js → [externals]__0v1hz15._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__1rqkg_y._.js → [externals]__1nb206a._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0--lkk6._.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]__0q0qzx1._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1le9jqc._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1p-qi2t._.js +1 -1
- package/.next/standalone/.next/server/chunks/{_08w6xzm._.js → _0otft92._.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 +3 -3
- package/.next/standalone/.next/server/chunks/_1vslkoe._.js +1 -1
- package/.next/standalone/.next/server/chunks/lib_factory-projects_ts_1eo_bk-._.js +1 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.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]__0s740oi._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0yl9wb5._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0guxnh-._.js +23 -0
- package/.next/standalone/.next/server/chunks/ssr/_0oq1dh6._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1_ozgif._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1es2j7i._.js +13 -0
- 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 -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/middleware-build-manifest.js +3 -3
- 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 +22 -22
- package/.next/standalone/.next/static/chunks/{04r6ch8uf_n8m.js → 04o5zfl7p5lhx.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0o-hh5_turzlz.css → 0jpd8dv2vk930.css} +1 -1
- package/.next/standalone/.next/static/chunks/{2ej3b8gk5ittu.js → 0po8n4tyotjf2.js} +1 -1
- package/.next/standalone/.next/static/chunks/0qx7gav1c2_vu.js +1 -0
- package/.next/standalone/.next/static/chunks/{2aquitk72k2op.js → 17tfki9x5rd_7.js} +1 -1
- package/.next/standalone/.next/static/chunks/{010bv1w6j171t.js → 1i-i0pxg9q_8y.js} +1 -1
- package/.next/standalone/.next/static/chunks/{32spub4wqjem-.js → 2bxr2_2h4kaqq.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0wz8yftk18ts2.js → 323cyb3a2s06y.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1pb1oztsbwcss.js → 364cqbpeg1y5i.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3m4upvybtrexd.js → 38dqur43qthrl.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2bi_1y0a_smt7.js → 42gwu2bsz4ske.js} +2 -2
- package/.next/standalone/app/actions/get-leaks.ts +10 -1
- package/.next/standalone/app/audit/_components/audit-dashboard.tsx +29 -2
- package/.next/standalone/app/audit/_components/leak-section.tsx +15 -2
- package/.next/standalone/app/audit/audit-styles.css +18 -0
- package/.next/standalone/lib/sqlite-reader.ts +78 -1
- package/.next/standalone/package.json +10 -10
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +1 -1
- package/dist/cli.mjs +397 -225
- package/dist/worker.mjs +81 -35
- package/lib/sqlite-reader.ts +78 -1
- package/package.json +10 -10
- package/pi-extension/index.ts +19 -4
- package/src/audit/cache.ts +6 -0
- package/src/audit/cli-adapters/claude.ts +13 -0
- package/src/audit/cli-login.ts +4 -1
- package/src/audit/cli.ts +84 -59
- package/src/audit/index.ts +149 -8
- package/src/audit/leak-notice.ts +99 -1
- package/src/audit/leak-scan.ts +20 -44
- package/src/audit/leak-store.ts +49 -1
- package/src/audit/redact-example.ts +60 -5
- package/src/audit/types.ts +6 -0
- package/src/hooks/handler.ts +30 -10
- package/src/hooks/integrations.ts +14 -0
- package/src/hooks/notice.ts +45 -9
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0rgu2r3._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0-oij9d._.js +0 -23
- package/.next/standalone/.next/static/chunks/0zebh1n9jkfbt.js +0 -1
- /package/.next/standalone/.next/static/{aKNv4Kmv98Xpwns0Xa-C- → P6icI2V2PvVuePJrqjHCr}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{aKNv4Kmv98Xpwns0Xa-C- → P6icI2V2PvVuePJrqjHCr}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{aKNv4Kmv98Xpwns0Xa-C- → P6icI2V2PvVuePJrqjHCr}/_ssgManifest.js +0 -0
package/src/audit/leak-scan.ts
CHANGED
|
@@ -24,18 +24,26 @@
|
|
|
24
24
|
* them apart so the report can tell a user which of the two happened.
|
|
25
25
|
*/
|
|
26
26
|
import { SECRET_PATTERNS } from "../hooks/builtin-policies";
|
|
27
|
-
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Cache key for the credential detector.
|
|
30
|
+
*
|
|
31
|
+
* The transcript cache used to key only on builtin policies and behavioural
|
|
32
|
+
* audit detectors. Changing THIS scanner therefore kept serving the old
|
|
33
|
+
* `result.leaks` forever, which made a precision fix appear to do nothing on a
|
|
34
|
+
* warm machine. Bump this whenever matching or suppression semantics change.
|
|
35
|
+
*/
|
|
36
|
+
export const LEAK_SCAN_VERSION = 5;
|
|
28
37
|
|
|
29
38
|
/** One credential value found in one event. */
|
|
30
39
|
export interface SecretMatch {
|
|
31
40
|
/** The raw value. Held in memory only — the caller fingerprints it and must
|
|
32
41
|
* never persist or transmit this. */
|
|
33
42
|
value: string;
|
|
34
|
-
/** The identifier it was assigned to, when
|
|
35
|
-
*
|
|
36
|
-
* pattern) this is the only actionable label there is. */
|
|
43
|
+
/** The identifier it was assigned to, when a recognized shaped value also
|
|
44
|
+
* appeared in an assignment. Used only as an actionable label. */
|
|
37
45
|
name: string | null;
|
|
38
|
-
/** Which
|
|
46
|
+
/** Which recognizable credential shape found it. */
|
|
39
47
|
rule: string;
|
|
40
48
|
/** Whether the value carried a recognised vendor shape. Drives the record's
|
|
41
49
|
* confidence, and therefore whether an alert may name a console. */
|
|
@@ -69,36 +77,6 @@ export interface SecretMatch {
|
|
|
69
77
|
const ASSIGNMENT_RE =
|
|
70
78
|
/(["']?)\b([A-Za-z_][A-Za-z0-9_]{0,127})\1([ \t]*(?::(?!\/\/)|=)[ \t]*)("[^"]*"|'[^']*'|[^\s;|&"']+)/g;
|
|
71
79
|
|
|
72
|
-
/** Shortest value worth reporting. Below this it is a flag, a boolean or a
|
|
73
|
-
* placeholder far more often than a credential. */
|
|
74
|
-
const MIN_VALUE_LEN = 8;
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Values that are structurally incapable of being a leaked credential.
|
|
78
|
-
*
|
|
79
|
-
* Deliberately structural, never entropy: the corpus's one confirmed-live
|
|
80
|
-
* password measures 3.19 bits per character while 1.3 million UUIDs in the same
|
|
81
|
-
* corpus sit at 3.72, so any entropy threshold that catches the password also
|
|
82
|
-
* catches every UUID. These tests are decisive instead — a `$VAR` reference
|
|
83
|
-
* contains no secret by construction, and `process.env.X` is the CORRECT secure
|
|
84
|
-
* form, which the redactor currently flags 7,870 times.
|
|
85
|
-
*/
|
|
86
|
-
function isNotACredential(value: string): boolean {
|
|
87
|
-
if (value.length < MIN_VALUE_LEN) return true;
|
|
88
|
-
// Shell/template indirection: the value is a reference, not a secret.
|
|
89
|
-
if (/^[$<{(`%]/.test(value)) return true;
|
|
90
|
-
if (value.includes("${") || value.includes("{{") || value.includes("%(")) return true;
|
|
91
|
-
// A code expression — `process.env.DISCORD_TOKEN` is how you SHOULD do it.
|
|
92
|
-
if (/^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*)+$/.test(value)) return true;
|
|
93
|
-
// Already masked, by us or by anyone else.
|
|
94
|
-
if (value.includes("[REDACTED") || /^[•*#.]+$/.test(value)) return true;
|
|
95
|
-
if (/(.)\1{5,}/.test(value)) return true; // xxxxxxxx, 00000000
|
|
96
|
-
if (/^(true|false|null|undefined|none)$/i.test(value)) return true;
|
|
97
|
-
if (/^-?\d+(\.\d+)?$/.test(value)) return true; // pure number
|
|
98
|
-
if (/^\/|^~\/|^\.\.?\//.test(value)) return true; // a path
|
|
99
|
-
return false;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
80
|
/** Strip one layer of surrounding quotes. */
|
|
103
81
|
function unquote(v: string): string {
|
|
104
82
|
return (v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))
|
|
@@ -249,19 +227,17 @@ export function findSecrets(text: string): SecretMatch[] {
|
|
|
249
227
|
}
|
|
250
228
|
}
|
|
251
229
|
|
|
230
|
+
// Names annotate a value the shape layer already proved; they no longer
|
|
231
|
+
// create findings. Even after increasingly elaborate value heuristics, the
|
|
232
|
+
// name-only lane still produced hundreds of code literals and test fixtures
|
|
233
|
+
// on the measured machine. The product now deliberately prefers missing an
|
|
234
|
+
// unrecognisable first-party password over telling somebody they leaked 500
|
|
235
|
+
// credentials they do not have.
|
|
252
236
|
for (const m of text.matchAll(ASSIGNMENT_RE)) {
|
|
253
237
|
const name = m[2];
|
|
254
238
|
const value = unquote(m[4]);
|
|
255
|
-
if (!isSecretName(name) || isNotACredential(value) || isDocsLiteral(value)) continue;
|
|
256
239
|
const existing = byValue.get(value);
|
|
257
|
-
if (existing)
|
|
258
|
-
// A vendor-shaped value that also has a name: keep the vendor rule (it
|
|
259
|
-
// can name a console) and gain the name (it says which of the user's
|
|
260
|
-
// variables to change).
|
|
261
|
-
existing.name ??= name;
|
|
262
|
-
continue;
|
|
263
|
-
}
|
|
264
|
-
byValue.set(value, { value, name, rule: "assigned secret", shaped: false });
|
|
240
|
+
if (existing) existing.name ??= name;
|
|
265
241
|
}
|
|
266
242
|
|
|
267
243
|
return [...byValue.values()];
|
package/src/audit/leak-store.ts
CHANGED
|
@@ -46,6 +46,24 @@ interface LeakIdentity {
|
|
|
46
46
|
salt: string;
|
|
47
47
|
/** Finding ids the user has said are not secrets. */
|
|
48
48
|
dismissed: string[];
|
|
49
|
+
/**
|
|
50
|
+
* When the user last actually LOOKED at the leak report, as epoch ms.
|
|
51
|
+
*
|
|
52
|
+
* This is what stops the in-CLI notice, and it replaces a per-finding "we
|
|
53
|
+
* emitted it once" marker that silently lost the alert twice in one day: once
|
|
54
|
+
* because it was claimed on an event whose channel the host ignores, and once
|
|
55
|
+
* because hooks were disabled in the project being tested. Both marked every
|
|
56
|
+
* finding delivered and showed nothing, with no retry, ever.
|
|
57
|
+
*
|
|
58
|
+
* Keyed on the user's own action rather than on ours, the failure direction
|
|
59
|
+
* inverts: a dropped notice simply reappears next session, and the only thing
|
|
60
|
+
* that silences it is the user opening the report — which is the outcome the
|
|
61
|
+
* notice exists to produce.
|
|
62
|
+
*
|
|
63
|
+
* `identity`, alongside `dismissed`, for the same reason: losing it re-alerts
|
|
64
|
+
* about credentials somebody has already seen and judged.
|
|
65
|
+
*/
|
|
66
|
+
reportViewedAt?: number;
|
|
49
67
|
}
|
|
50
68
|
|
|
51
69
|
/** Mode for both files. They carry no credential, but they do carry a machine
|
|
@@ -74,7 +92,14 @@ function readJson<T>(path: string): T | null {
|
|
|
74
92
|
export function readLeakIdentity(home?: string): LeakIdentity {
|
|
75
93
|
const existing = readJson<Partial<LeakIdentity>>(auditLeakIdentityFile(home));
|
|
76
94
|
if (existing && typeof existing.salt === "string" && existing.salt.length >= 32) {
|
|
77
|
-
return {
|
|
95
|
+
return {
|
|
96
|
+
salt: existing.salt,
|
|
97
|
+
dismissed: existing.dismissed ?? [],
|
|
98
|
+
reportViewedAt:
|
|
99
|
+
typeof existing.reportViewedAt === "number" && Number.isFinite(existing.reportViewedAt)
|
|
100
|
+
? existing.reportViewedAt
|
|
101
|
+
: undefined,
|
|
102
|
+
};
|
|
78
103
|
}
|
|
79
104
|
const minted: LeakIdentity = { salt: randomBytes(32).toString("hex"), dismissed: [] };
|
|
80
105
|
writeLeakIdentity(minted, home);
|
|
@@ -194,6 +219,29 @@ export function writeLeakRecord(record: LeakRecord, home?: string, nowMs = Date.
|
|
|
194
219
|
}
|
|
195
220
|
|
|
196
221
|
/** Mark a finding as not-a-secret. Survives a reset; see the module header. */
|
|
222
|
+
/**
|
|
223
|
+
* Record that the user has seen the leak report.
|
|
224
|
+
*
|
|
225
|
+
* Called from the dashboard when it reads the findings, and from the
|
|
226
|
+
* interactive `failproofai audit`, which prints them. Both are the user
|
|
227
|
+
* looking — which is the only signal worth silencing the notice on.
|
|
228
|
+
*
|
|
229
|
+
* Never throws: failing to record a view costs one extra notice, which is the
|
|
230
|
+
* side this whole design errs on.
|
|
231
|
+
*/
|
|
232
|
+
export function markLeakReportViewed(home?: string, nowMs = Date.now()): void {
|
|
233
|
+
try {
|
|
234
|
+
const identity = readLeakIdentity(home);
|
|
235
|
+
writeJsonAtomically(
|
|
236
|
+
auditLeakIdentityFile(home),
|
|
237
|
+
{ ...identity, reportViewedAt: nowMs },
|
|
238
|
+
{ mode: FILE_MODE },
|
|
239
|
+
);
|
|
240
|
+
} catch {
|
|
241
|
+
// See the doc comment: an unrecorded view re-notifies once.
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
197
245
|
export function dismissFinding(id: string, home?: string): boolean {
|
|
198
246
|
const identity = readLeakIdentity(home);
|
|
199
247
|
if (identity.dismissed.includes(id)) return true;
|
|
@@ -147,14 +147,40 @@ const SECRET_NAME_SUBSTRINGS = [
|
|
|
147
147
|
"APIKEY",
|
|
148
148
|
"PRIVATEKEY",
|
|
149
149
|
];
|
|
150
|
-
const SECRET_NAME_COMPONENTS = ["
|
|
150
|
+
const SECRET_NAME_COMPONENTS = ["PASS", "AUTH", "PAT", "SIG", "SIGNATURE", "COOKIE"];
|
|
151
151
|
|
|
152
152
|
/**
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
153
|
+
* Names that mean "credential" only inside a COMPOUND identifier.
|
|
154
|
+
*
|
|
155
|
+
* Every entry here was measured as a dominant false positive on a real 2,664-
|
|
156
|
+
* transcript corpus, and the reason they are demotions rather than deletions is
|
|
157
|
+
* that the SHAPE layer already catches real vendor keys regardless of what they
|
|
158
|
+
* are called. The name layer exists for FIRST-PARTY secrets with no recognisable
|
|
159
|
+
* format — and those essentially always carry a qualified name
|
|
160
|
+
* (`COMPOSIO_API_KEY`, `DB_PASSWORD`), never a bare one. So requiring a compound
|
|
161
|
+
* costs almost nothing real and removes most of the noise.
|
|
162
|
+
*
|
|
163
|
+
* PWD `MYSQL_PWD` is MySQL's documented password variable; a bare `PWD`
|
|
164
|
+
* is the shell's working directory, on every second line of a
|
|
165
|
+
* captured session.
|
|
166
|
+
* KEY a bare `key=` is a React list prop, a map entry, a sort field.
|
|
167
|
+
* 80 findings on the measured corpus, none of them credentials.
|
|
168
|
+
*
|
|
169
|
+
* `SIG` was demoted here too and put back: a bare `sig=` in a URL query IS a
|
|
170
|
+
* request signature, and an existing test says so. It also never appeared in
|
|
171
|
+
* the measured noise, unlike the two above — the rule is that a demotion needs
|
|
172
|
+
* evidence, not a plausible story.
|
|
173
|
+
*
|
|
174
|
+
* `SESSION` is REMOVED from both lists rather than demoted, and the difference
|
|
175
|
+
* matters: demoting it would have changed nothing, because
|
|
176
|
+
* `DBUS_SESSION_BUS_ADDRESS` is already a compound. It was the single largest
|
|
177
|
+
* source of noise measured — 254 of 500 findings, every one of them
|
|
178
|
+
* `DBUS_SESSION_BUS_ADDRESS`, `XDG_SESSION_TYPE`, `SESSION_MANAGER`,
|
|
179
|
+
* `session_id` or `sessionUpdate`. A session IDENTIFIER is not a rotatable
|
|
180
|
+
* credential; a session SECRET still matches through `SECRET`, `TOKEN`, or
|
|
181
|
+
* `KEY` in `sessionKey`.
|
|
156
182
|
*/
|
|
157
|
-
const COMPOUND_ONLY_COMPONENTS = ["PWD"];
|
|
183
|
+
const COMPOUND_ONLY_COMPONENTS = ["PWD", "KEY"];
|
|
158
184
|
|
|
159
185
|
/**
|
|
160
186
|
* Split an identifier into its uppercased components, on `_`, `-`, `.` AND
|
|
@@ -300,6 +326,35 @@ export function isPublishedByDesign(name: string): boolean {
|
|
|
300
326
|
return PUBLISHED_MARKERS.test(upper);
|
|
301
327
|
}
|
|
302
328
|
|
|
329
|
+
/**
|
|
330
|
+
* How confidently the NAME alone says "credential".
|
|
331
|
+
*
|
|
332
|
+
* `strong` — the word means secret and nothing else: `DB_PASSWORD`,
|
|
333
|
+
* `STRIPE_SECRET`, `PRIVATE_KEY`. A short value here is still worth flagging;
|
|
334
|
+
* `hunter2` assigned to `PASSWORD` is a leaked password.
|
|
335
|
+
*
|
|
336
|
+
* `weak` — the word is ALSO ordinary programming vocabulary: `key`, `token`,
|
|
337
|
+
* `auth`, `sig`. Measured on a real 2,664-transcript machine these produced
|
|
338
|
+
* `keyType`, `tokenLimitCancelled`, `max_output_tokens`, `resultKey`,
|
|
339
|
+
* `configDirKey` — 394 findings from this layer, of which 389 had no vendor
|
|
340
|
+
* prefix and 227 were under 24 characters. Nothing that short is an API key.
|
|
341
|
+
* A weak name has to be backed by a value that actually looks like a secret.
|
|
342
|
+
*/
|
|
343
|
+
export type SecretNameStrength = "strong" | "weak" | "none";
|
|
344
|
+
|
|
345
|
+
const STRONG_NAME_WORDS = ["SECRET", "PASSWORD", "PASSWD", "PASSPHRASE", "CREDENTIAL", "APIKEY", "PRIVATEKEY"];
|
|
346
|
+
|
|
347
|
+
export function secretNameStrength(name: string): SecretNameStrength {
|
|
348
|
+
if (!isSecretName(name)) return "none";
|
|
349
|
+
const upper = name.toUpperCase();
|
|
350
|
+
if (STRONG_NAME_WORDS.some((w) => upper.includes(w))) return "strong";
|
|
351
|
+
// `API_KEY` / `apiKey` split into components rather than matching APIKEY as a
|
|
352
|
+
// substring, and they are as unambiguous as the substring form.
|
|
353
|
+
const parts = identifierComponents(name);
|
|
354
|
+
if (parts.includes("API") && parts.includes("KEY")) return "strong";
|
|
355
|
+
return "weak";
|
|
356
|
+
}
|
|
357
|
+
|
|
303
358
|
export function isSecretName(name: string): boolean {
|
|
304
359
|
// Runs first and beats every other signal: a value the framework compiles
|
|
305
360
|
// into the browser bundle is public no matter what the rest of the name says.
|
package/src/audit/types.ts
CHANGED
|
@@ -45,6 +45,9 @@ export interface TranscriptMetadata {
|
|
|
45
45
|
mtimeMs: number;
|
|
46
46
|
/** Byte size of the transcript file. Used for cache invalidation. */
|
|
47
47
|
sizeBytes: number;
|
|
48
|
+
/** Optional host-authored session purpose. Used only to distinguish
|
|
49
|
+
* deliberate credential-scanner research from evidence of a leak. */
|
|
50
|
+
sessionDescription?: string;
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
/** Per-session detector state. Detectors mutate this freely. */
|
|
@@ -172,6 +175,9 @@ export interface TranscriptAuditResult {
|
|
|
172
175
|
* absent reads as "this transcript predates leak scanning", not "clean".
|
|
173
176
|
*/
|
|
174
177
|
leaks?: TranscriptLeak[];
|
|
178
|
+
/** The session was explicitly generating/researching credential fixtures,
|
|
179
|
+
* so its matches are examples rather than evidence of user leakage. */
|
|
180
|
+
leakScanSuppressed?: "credential-research";
|
|
175
181
|
/** First/last timestamp per name. */
|
|
176
182
|
rangeByName: Record<string, { first: string; last: string }>;
|
|
177
183
|
}
|
package/src/hooks/handler.ts
CHANGED
|
@@ -36,7 +36,7 @@ import type { CustomHook } from "./policy-types";
|
|
|
36
36
|
import { persistHookActivity } from "./hook-activity-store";
|
|
37
37
|
import { deliveryHealth, deliveryHealthLine } from "./delivery-health";
|
|
38
38
|
import { shapeNotice, canDeliverNotice, leakNoticeText } from "./notice";
|
|
39
|
-
import {
|
|
39
|
+
import { sessionNoticePending, markSessionNoticed } from "../audit/leak-notice";
|
|
40
40
|
import { trackHookEvent, flushHookTelemetry } from "./hook-telemetry";
|
|
41
41
|
import { resolveCwd } from "./resolve-cwd";
|
|
42
42
|
import { resolvePermissionMode } from "./resolve-permission-mode";
|
|
@@ -121,15 +121,34 @@ function attachLeakNotice(
|
|
|
121
121
|
eventType: string,
|
|
122
122
|
cli: IntegrationType | undefined,
|
|
123
123
|
outcome: HookEventOutcome,
|
|
124
|
+
sessionId: string | undefined,
|
|
124
125
|
): HookEventOutcome {
|
|
125
126
|
try {
|
|
126
127
|
if (!canDeliverNotice(cli)) return outcome;
|
|
127
128
|
// canDeliverNotice() above already proved cli is set; narrow for the call.
|
|
128
129
|
if (!cli) return outcome;
|
|
129
130
|
const canonical = canonicalizeEventType(eventType, cli);
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
// STOP ONLY, and the narrowing is the fix for a measured failure rather
|
|
132
|
+
// than caution. `notice.ts` derived each host's channel from a live probe
|
|
133
|
+
// on **Stop**; SessionStart was allowed here on the assumption that a
|
|
134
|
+
// channel proven on one event works on another. It does not. On a real
|
|
135
|
+
// machine 499 findings were CLAIMED and the user saw nothing: SessionStart
|
|
136
|
+
// fires first in a session, consumed every claim, and the host dropped the
|
|
137
|
+
// field — Claude Code documents `hookSpecificOutput.additionalContext` for
|
|
138
|
+
// SessionStart, not `systemMessage`.
|
|
139
|
+
//
|
|
140
|
+
// That is the worst outcome this design has: a finding recorded as
|
|
141
|
+
// delivered that reached nobody, and never retried. Stop fires at the end
|
|
142
|
+
// of every assistant turn, so nothing is lost by waiting for it — the user
|
|
143
|
+
// is reading output at that moment anyway. Add SessionStart back only with
|
|
144
|
+
// a probe showing the notice actually rendered there.
|
|
145
|
+
if (canonical !== "Stop") return outcome;
|
|
146
|
+
|
|
147
|
+
// Keyed on the SESSION and on whether the user has opened the report, not
|
|
148
|
+
// on a per-finding "we emitted this once" marker — see
|
|
149
|
+
// `sessionNoticePending` for the two ways that marker lost the alert
|
|
150
|
+
// permanently. A dropped notice now costs one session's silence instead.
|
|
151
|
+
const pending = sessionNoticePending(sessionId ?? "");
|
|
133
152
|
if (pending.count === 0) return outcome;
|
|
134
153
|
|
|
135
154
|
const shaped = shapeNotice(cli, leakNoticeText(pending.count), outcome.stdout);
|
|
@@ -137,7 +156,7 @@ function attachLeakNotice(
|
|
|
137
156
|
|
|
138
157
|
// Claimed only once the notice is actually on the stream, so a crash
|
|
139
158
|
// between the two re-notifies rather than silently swallowing the alert.
|
|
140
|
-
|
|
159
|
+
markSessionNoticed(sessionId ?? "");
|
|
141
160
|
return {
|
|
142
161
|
exitCode: outcome.exitCode,
|
|
143
162
|
stdout: shaped.stdout,
|
|
@@ -810,11 +829,12 @@ export async function evaluateHookEvent(
|
|
|
810
829
|
// the previous notice was dead code on every configured machine, and had
|
|
811
830
|
// been since it shipped. Both paths return this object, so producing the
|
|
812
831
|
// notice here reaches both with no change to either caller.
|
|
813
|
-
const withNotice = attachLeakNotice(
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
stderr: result.stderr,
|
|
817
|
-
|
|
832
|
+
const withNotice = attachLeakNotice(
|
|
833
|
+
eventType,
|
|
834
|
+
cli,
|
|
835
|
+
{ exitCode: result.exitCode, stdout: result.stdout, stderr: result.stderr },
|
|
836
|
+
sessionId,
|
|
837
|
+
);
|
|
818
838
|
return withNotice;
|
|
819
839
|
} finally {
|
|
820
840
|
if (opts?.awaitTelemetryFlush ?? true) {
|
|
@@ -784,6 +784,7 @@ export const cursor: Integration = {
|
|
|
784
784
|
// • exit 2 OR `permissionDecision: "deny"` → `throw new Error(reason)`
|
|
785
785
|
// (which OpenCode surfaces as a tool-call failure to the agent)
|
|
786
786
|
// • `additionalContext` → `client.session.prompt(...)` (fire-and-forget)
|
|
787
|
+
// • `failproofaiNotice` → `client.tui.showToast(...)` (user-visible)
|
|
787
788
|
// • everything else → no-op (allow)
|
|
788
789
|
//
|
|
789
790
|
// Settings paths:
|
|
@@ -956,6 +957,19 @@ async function applyDecision(result, ctx, eventName) {
|
|
|
956
957
|
if (out && out.decision && out.decision.behavior === "deny") {
|
|
957
958
|
throw new Error((out.decision.message) || "Blocked by failproofai");
|
|
958
959
|
}
|
|
960
|
+
const notice = parsed.failproofaiNotice;
|
|
961
|
+
if (typeof notice === "string" && notice && ctx && ctx.client && ctx.client.tui) {
|
|
962
|
+
try {
|
|
963
|
+
await ctx.client.tui.showToast({
|
|
964
|
+
body: {
|
|
965
|
+
title: "failproofai audit",
|
|
966
|
+
message: notice,
|
|
967
|
+
variant: "warning",
|
|
968
|
+
duration: 12_000,
|
|
969
|
+
},
|
|
970
|
+
});
|
|
971
|
+
} catch { /* a courtesy notice must never break the host hook */ }
|
|
972
|
+
}
|
|
959
973
|
// Forward additional context as a prompt to the session. For Stop /
|
|
960
974
|
// SubagentStop the prompt is the only force-retry channel (session.idle
|
|
961
975
|
// already fired), so AWAIT to ensure the SDK round-trip completes before
|
package/src/hooks/notice.ts
CHANGED
|
@@ -28,16 +28,17 @@
|
|
|
28
28
|
* copilot exit 0 + stderr renders as a `!` session warning
|
|
29
29
|
* factory plain stdout renders under a `Hooks Stop` block
|
|
30
30
|
*
|
|
31
|
-
* opencode
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
31
|
+
* opencode and pi have real channels too, but they live inside the plugin shims
|
|
32
|
+
* we ship rather than in a hook's stdout, so a private JSON field is consumed
|
|
33
|
+
* there and handed to `showToast` / `ui.notify`. OpenClaw, cursor, devin, goose,
|
|
34
|
+
* antigravity and hermes have no verified channel that reaches a human without
|
|
35
|
+
* an unsupported escape hatch — they get nothing here, and their users get the
|
|
36
|
+
* finding from `failproofai audit` and the scheduled desktop notification.
|
|
36
37
|
*/
|
|
37
38
|
import type { IntegrationType } from "./types";
|
|
38
39
|
|
|
39
40
|
/** How a notice is delivered on one CLI. */
|
|
40
|
-
type NoticeChannel = "systemMessage" | "stderr" | "stdout" | "none";
|
|
41
|
+
type NoticeChannel = "systemMessage" | "stderr" | "stdout" | "shim" | "none";
|
|
41
42
|
|
|
42
43
|
/**
|
|
43
44
|
* Per-CLI channel, from live probes rather than documentation.
|
|
@@ -52,6 +53,8 @@ const NOTICE_CHANNEL: Partial<Record<IntegrationType, NoticeChannel>> = {
|
|
|
52
53
|
codex: "systemMessage",
|
|
53
54
|
copilot: "stderr",
|
|
54
55
|
factory: "stdout",
|
|
56
|
+
opencode: "shim",
|
|
57
|
+
pi: "shim",
|
|
55
58
|
};
|
|
56
59
|
|
|
57
60
|
/**
|
|
@@ -123,6 +126,23 @@ export function shapeNotice(
|
|
|
123
126
|
// verdict JSON is already on the stream.
|
|
124
127
|
if (existingStdout.trim()) return empty;
|
|
125
128
|
return { stdout: text + "\n", stderr: "" };
|
|
129
|
+
case "shim": {
|
|
130
|
+
// OpenCode and Pi are in-process plugin integrations. Their generated
|
|
131
|
+
// shims consume this private field and call the host's real user-facing
|
|
132
|
+
// notification API; putting the text in additionalContext would send it
|
|
133
|
+
// to the model instead.
|
|
134
|
+
if (existingStdout.trim().startsWith("{")) {
|
|
135
|
+
try {
|
|
136
|
+
const parsed = JSON.parse(existingStdout) as Record<string, unknown>;
|
|
137
|
+
parsed.failproofaiNotice = text;
|
|
138
|
+
return { stdout: JSON.stringify(parsed), stderr: "" };
|
|
139
|
+
} catch {
|
|
140
|
+
return empty;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (existingStdout.trim()) return empty;
|
|
144
|
+
return { stdout: JSON.stringify({ failproofaiNotice: text }), stderr: "" };
|
|
145
|
+
}
|
|
126
146
|
default:
|
|
127
147
|
return empty;
|
|
128
148
|
}
|
|
@@ -146,10 +166,26 @@ export function canDeliverNotice(cli: IntegrationType | undefined): boolean {
|
|
|
146
166
|
* directly in the prompt. So nothing scanned ever reaches this template.
|
|
147
167
|
*/
|
|
148
168
|
export function leakNoticeText(newFindings: number): string {
|
|
149
|
-
const n = newFindings === 1
|
|
169
|
+
const n = newFindings === 1
|
|
170
|
+
? "a possible credential exposure"
|
|
171
|
+
: `${newFindings} possible credential exposures`;
|
|
150
172
|
return (
|
|
151
173
|
`failproofai found ${n} in this project's agent transcripts.\n` +
|
|
152
|
-
"Run `failproofai audit` to
|
|
153
|
-
"or `failproofai audit --schedule` to get these by email."
|
|
174
|
+
"Run `failproofai audit` to review the matches and rotate anything real."
|
|
154
175
|
);
|
|
155
176
|
}
|
|
177
|
+
|
|
178
|
+
/** Copy for the OS notification raised by an unattended scheduled scan.
|
|
179
|
+
*
|
|
180
|
+
* Deliberately omits the count. A burst of generated fixtures can hit the
|
|
181
|
+
* record cap, and a desktop banner saying "500 leaked credentials" states a
|
|
182
|
+
* conclusion the detector cannot support. The dashboard carries the evidence;
|
|
183
|
+
* the banner's job is only to ask for review. */
|
|
184
|
+
export function desktopLeakNotice(): { title: string; body: string } {
|
|
185
|
+
return {
|
|
186
|
+
title: "failproofai audit needs review",
|
|
187
|
+
body:
|
|
188
|
+
"Possible credential exposure was found in your agent transcripts. " +
|
|
189
|
+
"Run failproofai audit to verify the matches and rotate anything real.",
|
|
190
|
+
};
|
|
191
|
+
}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
module.exports=[18622,(a,b,c)=>{b.exports=a.x("next/dist/compiled/next-server/app-page-turbo.runtime.prod.js",()=>require("next/dist/compiled/next-server/app-page-turbo.runtime.prod.js"))},20635,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/action-async-storage.external.js",()=>require("next/dist/server/app-render/action-async-storage.external.js"))},24725,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/after-task-async-storage.external.js",()=>require("next/dist/server/app-render/after-task-async-storage.external.js"))},43285,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/dynamic-access-async-storage.external.js",()=>require("next/dist/server/app-render/dynamic-access-async-storage.external.js"))},56704,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/work-async-storage.external.js",()=>require("next/dist/server/app-render/work-async-storage.external.js"))},32319,(a,b,c)=>{b.exports=a.x("next/dist/server/app-render/work-unit-async-storage.external.js",()=>require("next/dist/server/app-render/work-unit-async-storage.external.js"))},59043,(a,b,c)=>{b.exports=a.x("next/dist/server/runtime-reacts.external.js",()=>require("next/dist/server/runtime-reacts.external.js"))},1355,a=>{"use strict";var b=a.i(5050);let c=(0,b.createServerReference)("000ae5b9f039195740b0697383b66ef36475abe12d",b.callServer,void 0,b.findSourceMapURL,"getTelemetryConfig");a.s(["getTelemetryConfig",0,c])},3171,a=>{"use strict";var b=a.i(87924),c=a.i(72131),d=a.i(1355),e=a.i(66036);a.s(["default",0,function({error:a,reset:f}){return(0,c.useEffect)(()=>{(0,d.getTelemetryConfig)().then(b=>{(0,e.setClientTelemetryConfig)(b),(0,e.captureClientEvent)("client_error",{error_message:a.message,error_name:a.name,error_digest:a.digest,boundary:"global"})}).catch(()=>{})},[a]),(0,b.jsx)("html",{children:(0,b.jsx)("body",{children:(0,b.jsx)("main",{style:{minHeight:"100vh",display:"flex",alignItems:"center",justifyContent:"center",background:"#031035",color:"#f8fafc",fontFamily:"system-ui, sans-serif"},children:(0,b.jsxs)("div",{style:{textAlign:"center",padding:"2rem",border:"1px solid rgba(239,68,68,0.4)",borderRadius:"0.5rem",maxWidth:"500px"},children:[(0,b.jsx)("h2",{style:{color:"#ef4444",marginBottom:"0.5rem",fontSize:"1.25rem"},children:"Something went wrong"}),(0,b.jsx)("p",{style:{color:"#94a3b8",marginBottom:"1.5rem"},children:a.message||"An unexpected error occurred."}),(0,b.jsx)("button",{onClick:f,style:{padding:"0.5rem 1.25rem",background:"#3b82f6",color:"white",border:"none",borderRadius:"0.375rem",cursor:"pointer",fontSize:"0.875rem"},children:"Try again"})]})})})})}])},66036,a=>{"use strict";a.s(["captureClientEvent",0,function(a,b){},"setClientTelemetryConfig",0,function(a){}])},46058,(a,b,c)=>{"use strict";function d(a){if("function"!=typeof WeakMap)return null;var b=new WeakMap,c=new WeakMap;return(d=function(a){return a?c:b})(a)}c._=function(a,b){if(!b&&a&&a.__esModule)return a;if(null===a||"object"!=typeof a&&"function"!=typeof a)return{default:a};var c=d(b);if(c&&c.has(a))return c.get(a);var e={__proto__:null},f=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var g in a)if("default"!==g&&Object.prototype.hasOwnProperty.call(a,g)){var h=f?Object.getOwnPropertyDescriptor(a,g):null;h&&(h.get||h.set)?Object.defineProperty(e,g,h):e[g]=a[g]}return e.default=a,c&&c.set(a,e),e}},5050,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d={callServer:function(){return f.callServer},createServerReference:function(){return h.createServerReference},findSourceMapURL:function(){return g.findSourceMapURL}};for(var e in d)Object.defineProperty(c,e,{enumerable:!0,get:d[e]});let f=a.r(20611),g=a.r(1722),h=a.r(38783)},20611,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"callServer",{enumerable:!0,get:function(){return g}});let d=a.r(72131),e=a.r(88347),f=a.r(90841);async function g(a,b){return new Promise((c,g)=>{(0,d.startTransition)(()=>{(0,f.dispatchAppRouterAction)({type:e.ACTION_SERVER_ACTION,actionId:a,actionArgs:b,resolve:c,reject:g})})})}("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},1722,(a,b,c)=>{"use strict";let d;Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"findSourceMapURL",{enumerable:!0,get:function(){return d}});("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},88347,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d,e,f={ACTION_HMR_REFRESH:function(){return l},ACTION_NAVIGATE:function(){return i},ACTION_REFRESH:function(){return h},ACTION_RESTORE:function(){return j},ACTION_SERVER_ACTION:function(){return m},ACTION_SERVER_PATCH:function(){return k},PrefetchKind:function(){return n},ScrollBehavior:function(){return o}};for(var g in f)Object.defineProperty(c,g,{enumerable:!0,get:f[g]});let h="refresh",i="navigate",j="restore",k="server-patch",l="hmr-refresh",m="server-action";var n=((d={}).AUTO="auto",d.FULL="full",d),o=((e={})[e.Default=0]="Default",e[e.NoScroll=1]="NoScroll",e);("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},90841,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d={dispatchAppRouterAction:function(){return i},dispatchGestureState:function(){return j},refreshOnInstantNavigationUnlock:function(){return h},useActionQueue:function(){return k}};for(var e in d)Object.defineProperty(c,e,{enumerable:!0,get:d[e]});let f=a.r(46058)._(a.r(72131)),g=a.r(67009);a.r(88347);function h(){}function i(a){!0;throw Object.defineProperty(Error("Internal Next.js error: Router action dispatched before initialization."),"__NEXT_ERROR_CODE",{value:"E668",enumerable:!1,configurable:!0})}function j(a){!0;throw Object.defineProperty(Error("Internal Next.js error: Router action dispatched before initialization."),"__NEXT_ERROR_CODE",{value:"E668",enumerable:!1,configurable:!0})}function k(a){let[b,c]=f.default.useState(a.state),[d,e]=(0,f.useOptimistic)(b),h=(0,f.useMemo)(()=>d,[d]);return(0,g.isThenable)(h)?(0,f.use)(h):h}("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},42602,(a,b,c)=>{"use strict";b.exports=a.r(18622)},9270,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored.contexts.AppRouterContext},36313,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored.contexts.HooksClientContext},18341,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored.contexts.ServerInsertedHtml},35112,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored["react-ssr"].ReactDOM},87924,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored["react-ssr"].ReactJsxRuntime},38783,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored["react-ssr"].ReactServerDOMTurbopackClient},72131,(a,b,c)=>{"use strict";b.exports=a.r(42602).vendored["react-ssr"].React},67009,(a,b,c)=>{"use strict";function d(a){return null!==a&&"object"==typeof a&&"then"in a&&"function"==typeof a.then}Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"isThenable",{enumerable:!0,get:function(){return d}})}];
|
|
2
|
-
|
|
3
|
-
//# sourceMappingURL=%5Broot-of-the-server%5D__0rgu2r3._.js.map
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
module.exports=[1355,a=>{"use strict";var b=a.i(5050);let c=(0,b.createServerReference)("000ae5b9f039195740b0697383b66ef36475abe12d",b.callServer,void 0,b.findSourceMapURL,"getTelemetryConfig");a.s(["getTelemetryConfig",0,c])},18952,a=>{"use strict";var b=a.i(87924),c=a.i(72131);let d=[],e=0,f=new Set;function g(){for(let a of f)a()}function h(a){return f.add(a),()=>f.delete(a)}function i(){return d}function j({message:a}){let[d,e]=(0,c.useState)(!1);return(0,c.useEffect)(()=>{requestAnimationFrame(()=>e(!0));let a=setTimeout(()=>e(!1),2e3);return()=>clearTimeout(a)},[]),(0,b.jsx)("div",{className:`bg-card border border-border text-foreground text-sm px-4 py-2 rounded-lg shadow-lg transition-opacity duration-300 ${d?"opacity-100":"opacity-0"}`,children:a})}a.s(["Toaster",0,function(){let a=(0,c.useSyncExternalStore)(h,i,i);return(0,b.jsx)("div",{className:"fixed bottom-4 right-4 z-50 flex flex-col gap-2",children:a.map(a=>(0,b.jsx)(j,{message:a.message},a.id))})},"toast",0,function(a){let b=e++;d=[...d,{id:b,message:a}],g(),setTimeout(()=>{d=d.filter(a=>a.id!==b),g()},2500)}])},10765,a=>{"use strict";var b=a.i(87924),c=a.i(72131),d=a.i(50944),e=a.i(1355),f=a.i(66036);let g=(0,c.createContext)(void 0);a.s(["PostHogProvider",0,function({children:a}){let h=(0,d.usePathname)(),i=(0,c.useRef)(null),j=(0,c.useRef)(!1);(0,c.useEffect)(()=>{let a=!1;return(0,e.getTelemetryConfig)().then(b=>{a||((0,f.setClientTelemetryConfig)(b),j.current=!0,(0,f.captureClientEvent)("$pageview"),i.current=window.location.pathname)}),()=>{a=!0}},[]),(0,c.useEffect)(()=>{j.current&&i.current!==h&&(i.current=h,(0,f.captureClientEvent)("$pageview"))},[h]);let k=c.default.useCallback((a,b)=>{(0,f.captureClientEvent)(a,b)},[]);return(0,b.jsx)(g.Provider,{value:{capture:k},children:a})},"usePostHog",0,function(){let a=(0,c.useContext)(g);if(void 0===a)throw Error("usePostHog must be used within a PostHogProvider");return a}])},66036,a=>{"use strict";a.s(["captureClientEvent",0,function(a,b){},"setClientTelemetryConfig",0,function(a){}])},46058,(a,b,c)=>{"use strict";function d(a){if("function"!=typeof WeakMap)return null;var b=new WeakMap,c=new WeakMap;return(d=function(a){return a?c:b})(a)}c._=function(a,b){if(!b&&a&&a.__esModule)return a;if(null===a||"object"!=typeof a&&"function"!=typeof a)return{default:a};var c=d(b);if(c&&c.has(a))return c.get(a);var e={__proto__:null},f=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var g in a)if("default"!==g&&Object.prototype.hasOwnProperty.call(a,g)){var h=f?Object.getOwnPropertyDescriptor(a,g):null;h&&(h.get||h.set)?Object.defineProperty(e,g,h):e[g]=a[g]}return e.default=a,c&&c.set(a,e),e}},5050,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d={callServer:function(){return f.callServer},createServerReference:function(){return h.createServerReference},findSourceMapURL:function(){return g.findSourceMapURL}};for(var e in d)Object.defineProperty(c,e,{enumerable:!0,get:d[e]});let f=a.r(20611),g=a.r(1722),h=a.r(38783)},20611,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"callServer",{enumerable:!0,get:function(){return g}});let d=a.r(72131),e=a.r(88347),f=a.r(90841);async function g(a,b){return new Promise((c,g)=>{(0,d.startTransition)(()=>{(0,f.dispatchAppRouterAction)({type:e.ACTION_SERVER_ACTION,actionId:a,actionArgs:b,resolve:c,reject:g})})})}("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},1722,(a,b,c)=>{"use strict";let d;Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"findSourceMapURL",{enumerable:!0,get:function(){return d}});("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},88347,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d,e,f={ACTION_HMR_REFRESH:function(){return l},ACTION_NAVIGATE:function(){return i},ACTION_REFRESH:function(){return h},ACTION_RESTORE:function(){return j},ACTION_SERVER_ACTION:function(){return m},ACTION_SERVER_PATCH:function(){return k},PrefetchKind:function(){return n},ScrollBehavior:function(){return o}};for(var g in f)Object.defineProperty(c,g,{enumerable:!0,get:f[g]});let h="refresh",i="navigate",j="restore",k="server-patch",l="hmr-refresh",m="server-action";var n=((d={}).AUTO="auto",d.FULL="full",d),o=((e={})[e.Default=0]="Default",e[e.NoScroll=1]="NoScroll",e);("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},90841,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d={dispatchAppRouterAction:function(){return i},dispatchGestureState:function(){return j},refreshOnInstantNavigationUnlock:function(){return h},useActionQueue:function(){return k}};for(var e in d)Object.defineProperty(c,e,{enumerable:!0,get:d[e]});let f=a.r(46058)._(a.r(72131)),g=a.r(67009);a.r(88347);function h(){}function i(a){!0;throw Object.defineProperty(Error("Internal Next.js error: Router action dispatched before initialization."),"__NEXT_ERROR_CODE",{value:"E668",enumerable:!1,configurable:!0})}function j(a){!0;throw Object.defineProperty(Error("Internal Next.js error: Router action dispatched before initialization."),"__NEXT_ERROR_CODE",{value:"E668",enumerable:!1,configurable:!0})}function k(a){let[b,c]=f.default.useState(a.state),[d,e]=(0,f.useOptimistic)(b),h=(0,f.useMemo)(()=>d,[d]);return(0,g.isThenable)(h)?(0,f.use)(h):h}("function"==typeof c.default||"object"==typeof c.default&&null!==c.default)&&void 0===c.default.__esModule&&(Object.defineProperty(c.default,"__esModule",{value:!0}),Object.assign(c.default,c),b.exports=c.default)},14827,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d={createLinkPrefetchPartialError:function(){return g},createUnrenderedSegmentError:function(){return f}};for(var e in d)Object.defineProperty(c,e,{enumerable:!0,get:d[e]});function f(a,b){let c=`Route "${a}": Could not validate that a segment in your UI has instant navigation.`;if(b.length>0){let a=1===b.length?"Dropped segment":"Dropped segments";c+=`
|
|
2
|
-
|
|
3
|
-
This segment was dropped from rendering. Issues that would prevent instant navigation will go undetected.
|
|
4
|
-
|
|
5
|
-
${a}:
|
|
6
|
-
${b.map(a=>` ${a}`).join("\n")}
|
|
7
|
-
|
|
8
|
-
Ways to fix this:
|
|
9
|
-
- [render] Render the dropped segment
|
|
10
|
-
- [ignore] Set \`export const instant = false\` to opt the dropped segment out of instant-navigation validation
|
|
11
|
-
|
|
12
|
-
Learn more: https://nextjs.org/docs/messages/instant-unrendered-segment`}return Object.defineProperty(Error(c),"__NEXT_ERROR_CODE",{value:"E1286",enumerable:!1,configurable:!0})}function g(a){return Object.defineProperty(Error(`Next.js encountered dynamic data during prefetching for "${a}".
|
|
13
|
-
|
|
14
|
-
This will lead to slower, more expensive prefetches.
|
|
15
|
-
|
|
16
|
-
Ways to fix this:
|
|
17
|
-
- [upgrade] Opt into Partial Prefetching by exporting \`const prefetch = 'partial'\` from the page or layout, or by setting \`partialPrefetching: true\` in next.config to opt the whole app in
|
|
18
|
-
- [disable] Remove \`prefetch={true}\` from the <Link> to use the default prefetch
|
|
19
|
-
- [ignore] Set \`export const instant = false\` to opt the route out of instant-navigation validation
|
|
20
|
-
|
|
21
|
-
Learn more: https://nextjs.org/docs/messages/instant-link-prefetch-partial`),"__NEXT_ERROR_CODE",{value:"E1435",enumerable:!1,configurable:!0})}},88644,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"InvariantError",{enumerable:!0,get:function(){return d}});class d extends Error{constructor(a,b){super(`Invariant: ${a.endsWith(".")?a:a+"."} This is a bug in Next.js.`,b),Object.defineProperty(this,"__NEXT_ERROR_CODE",{value:"E1179",enumerable:!1,configurable:!0}),this.name="InvariantError"}}},67009,(a,b,c)=>{"use strict";function d(a){return null!==a&&"object"==typeof a&&"then"in a&&"function"==typeof a.then}Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"isThenable",{enumerable:!0,get:function(){return d}})},41997,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d={BailoutToCSRError:function(){return g},isBailoutToCSRError:function(){return h}};for(var e in d)Object.defineProperty(c,e,{enumerable:!0,get:d[e]});let f="BAILOUT_TO_CLIENT_SIDE_RENDERING";class g extends Error{constructor(a){super(`Bail out to client-side rendering: ${a}`),this.reason=a,this.digest=f}}function h(a){return"object"==typeof a&&null!==a&&"digest"in a&&a.digest===f}},54427,(a,b,c)=>{"use strict";function d(){let a,b,c=new Promise((c,d)=>{a=c,b=d});return{resolve:a,reject:b,promise:c}}Object.defineProperty(c,"__esModule",{value:!0}),Object.defineProperty(c,"createPromiseWithResolvers",{enumerable:!0,get:function(){return d}})},39118,(a,b,c)=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d={DEFAULT_SEGMENT_KEY:function(){return l},NOT_FOUND_SEGMENT_KEY:function(){return m},PAGE_SEGMENT_KEY:function(){return k},addSearchParamsIfPageSegment:function(){return i},computeSelectedLayoutSegment:function(){return j},getSegmentValue:function(){return f},getSelectedLayoutSegmentPath:function(){return function a(b,c,d=!0,e=[]){let g;if(d)g=b[1][c];else{let a=b[1];g=a.children??Object.values(a)[0]}if(!g)return e;let h=f(g[0]);return!h||h.startsWith(k)?e:(e.push(h),a(g,c,!1,e))}},isGroupSegment:function(){return g},isParallelRouteSegment:function(){return h}};for(var e in d)Object.defineProperty(c,e,{enumerable:!0,get:d[e]});function f(a){return Array.isArray(a)?a[1]:a}function g(a){return"("===a[0]&&a.endsWith(")")}function h(a){return a.startsWith("@")&&"@children"!==a}function i(a,b){if(a.includes(k)){let a=JSON.stringify(b);return"{}"!==a?k+"?"+a:k}return a}function j(a,b){if(!a||0===a.length)return null;let c="children"===b?a[0]:a[a.length-1];return c===l?null:c}let k="__PAGE__",l="__DEFAULT__",m="/_not-found"}];
|
|
22
|
-
|
|
23
|
-
//# sourceMappingURL=_0-oij9d._.js.map
|
|
@@ -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),t=e.i(95187);let n=(0,t.createServerReference)("00840e34fdc0ab0093541d34b83cc649ce4f5d86f3",t.callServer,void 0,t.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"),t=["jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"][s.getUTCMonth()],n=s.getUTCFullYear(),r=s.getUTCHours().toString().padStart(2,"0"),i=s.getUTCMinutes().toString().padStart(2,"0");return`${a} ${t} ${n}, ${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,t.createServerReference)("40d9250b20c84e4d86f5898290e4a7a464c019b309",t.callServer,void 0,t.findSourceMapURL,"dismissLeakAction"),c=(0,t.createServerReference)("00290d6c5d144e8bf121d1b8e95c08990b6bafca27",t.callServer,void 0,t.findSourceMapURL,"getLeaksAction"),d=(0,t.createServerReference)("40f2667668aae1ab9d2a5b00c81eebc40f91a3fc8b",t.callServer,void 0,t.findSourceMapURL,"setLeakNotifyAction");function o(){let[e,t]=(0,a.useState)(null),[n,r]=(0,a.useState)(!0),[i,o]=(0,a.useState)(null),u=(0,a.useCallback)(async()=>{let e=await c();t(e.rows),r(e.notify)},[]);(0,a.useEffect)(()=>{u()},[u]);let h=(0,a.useCallback)(async e=>{o(e);try{await l(e)&&t(s=>s?.filter(s=>s.id!==e)??null)}finally{o(null)}},[]),p=(0,a.useCallback)(async()=>{let e=!n;r(e),await d(e)||r(!e)},[n]);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":n,onClick:p,"aria-label":n?"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 h(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})," · ",(0,s.jsx)("span",{className:"leak-when",children:function(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"})}(e.lastSeen)})]}),(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 u=e.i(86616);function h({mode:e,running:a,onStarted:t,onCompleted:n}){let{capture:i}=(0,r.usePostHog)(),l=async()=>{i("audit_first_run_clicked",{source:"empty_state",mode:e}),t();try{await (0,u.triggerRun)({cli:[],since:"all"})}catch(e){console.error("audit first run failed:",e)}finally{await n()}};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 t=["1","5","8","10","13","17","20","23","25","30","32"].includes(String(a));return(0,s.jsx)("span",{className:"px"+(t?" 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 your agent's archetype and a punch-list."]}),(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 t=["2","4","9","11","16","18","23","25","27","30","33"].includes(String(a));return(0,s.jsx)("span",{className:"px"+(t?" 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 m(){let[e,t]=(0,a.useState)(0),[n,r]=(0,a.useState)(0);(0,a.useEffect)(()=>{let e=setInterval(()=>{t(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(n+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,t)=>{let r=t<e,i=t===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:["⠋","⠙","⠹","⠸"][n]})]},t)})}),(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 g={timeout:"audit took too long — try again",network:"network hiccup — check your connection",post_failed:"audit failed — try again"};function x({status:e,onDismiss:t}){let[n,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,n-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"),t=(s%60).toString().padStart(2,"0");return`${a}:${t}`}(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:"×"}),g[e.reason]]}),(0,s.jsx)("button",{type:"button",className:"audit-progress-strip__dismiss",onClick:t,"aria-label":"dismiss",children:"[ dismiss ]"})]})})}function f({result:e,cachedAt:t,projectFromUrl:n,isRunning:l,rerunStatus:c,onRerun:d,onDismissRerun:u}){let{capture:h}=(0,r.usePostHog)(),{detectorsTriggered:p,missing:m}=(0,a.useMemo)(()=>{let s=0,a=0;for(let t of e.results)t.hits>0&&s++,"builtin"===t.source&&!t.enabledInConfig&&t.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:m,transcripts_scanned:e.transcripts.scanned,results_count:e.results.length,detectors_triggered:p}))},[h,m,e.transcripts.scanned,e.results.length,p]),(0,s.jsxs)("div",{className:"app",children:[(0,s.jsx)(x,{status:c,onDismiss:u}),(0,s.jsxs)("div",{className:"app-shell",children:[(0,s.jsxs)("div",{className:"report",children:[(0,s.jsx)(j,{transcripts:e.transcripts.scanned,events:e.eventsScanned??0,projects:e.projectsScanned.length}),(0,s.jsx)(o,{})]}),(0,s.jsx)(i,{cachedAt:t})]})]})}function j({transcripts:e,events:a,projects:t}){return(0,s.jsxs)("section",{className:"audit-sec","data-screen-label":"01 Scan",children:[(0,s.jsx)("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)("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",t>0?` \xb7 ${t.toLocaleString()} project${1===t?"":"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 y({running:e,mode:a="no-cache",rerunStatus:t,onDismissRerun:n,onStarted:r,onCompleted:l}){return(0,s.jsxs)("div",{className:"app",children:[(0,s.jsx)(x,{status:t,onDismiss:n}),(0,s.jsxs)("div",{className:"app-shell",children:[(0,s.jsx)("div",{className:"report",children:e?(0,s.jsx)(m,{}):(0,s.jsx)(h,{mode:a,running:e,onStarted:r,onCompleted:l})}),(0,s.jsx)(i,{cachedAt:null,fixed:!0})]})]})}e.s(["AuditDashboard",0,function({initial:e,projectFromUrl:t,totalCatalogSize:i}){let[l,c]=(0,a.useState)(e),[d,o]=(0,a.useState)(!1),[h,p]=(0,a.useState)({kind:"idle"}),{capture:m}=(0,r.usePostHog)(),g=(0,a.useRef)(null),x=(0,a.useRef)(!1),j="cached"===l.status?l.result.transcripts.scanned:0,b="cached"===l.status?l.result.results.length:0,N=(0,a.useCallback)(async()=>{c(await n())},[]),v=(0,a.useCallback)(async e=>{if(d)return;m("audit_rerun_clicked",{source:e,since:"all"}),o(!0);let s=Date.now();p({kind:"running",startedAt:s});try{await (0,u.triggerRun)({cli:[],since:"all",noCache:!0}),await N(),m("audit_rerun_succeeded",{source:e,since:"all",duration_ms:Date.now()-s}),p({kind:"idle"})}catch(t){let a=t instanceof u.RerunError?t.kind:"network";m("audit_rerun_failed",{kind:a,source:e,since:"all",cli_filter:"all",duration_ms:Date.now()-s}),p({kind:"failed",reason:a,failedAt:Date.now()})}finally{o(!1)}},[m,N,d]),k=(0,a.useCallback)(()=>{p({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===j?"zero_sessions":"report";g.current!==e&&(g.current=e,m("audit_page_viewed",{state:e,has_cache:"cached"===l.status}))},[l.status,m,d,j]),(0,a.useEffect)(()=>{x.current=!1},[l.status,d,j]),(0,a.useEffect)(()=>{if(d||"cached"!==l.status||0===j)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,m("audit_page_scrolled_to_end",{results_count:b,transcripts_scanned:j}))},a=()=>{0===e&&(e=requestAnimationFrame(s))};return s(),window.addEventListener("scroll",a,{passive:!0}),()=>{window.removeEventListener("scroll",a),0!==e&&cancelAnimationFrame(e)}},[l.status,m,b,d,j]),"empty"===l.status&&!d)return(0,s.jsx)(y,{running:!1,rerunStatus:h,onDismissRerun:k,onStarted:()=>o(!0),onCompleted:async()=>{o(!1),await N()}});if("empty"===l.status&&d)return(0,s.jsx)(y,{running:!0,rerunStatus:h,onDismissRerun:k,onStarted:()=>{},onCompleted:async()=>{o(!1),await N()}});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)(y,{running:d,mode:"zero-sessions",rerunStatus:h,onDismissRerun:k,onStarted:()=>o(!0),onCompleted:async()=>{o(!1),await N()}}):(0,s.jsx)(f,{result:w,cachedAt:S,projectFromUrl:t,isRunning:d,rerunStatus:h,onRerun:e=>v(e),onDismissRerun:k})}],24358)},86616,29054,e=>{"use strict";function s(e,a={},t=15e3){let n=AbortSignal.timeout(t),r=a.signal?AbortSignal.any([a.signal,n]):n;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 t extends Error{kind;constructor(e,s){super(s),this.kind=e,this.name="RerunError"}}async function n(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 t("post_failed",`audit run failed (${a.status})`)}}catch(e){if(e instanceof t)throw e;throw console.error("audit run request failed:",e),new t(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 s("/api/audit/status",{cache:"no-store"});a.ok&&(e=await a.json())}catch{}if(null===e){if(++n>=10)throw new t("network","audit status poll lost the server");continue}if(n=0,!e.running){if(e.error)throw new t("post_failed",e.error);return}}}e.s(["RerunError",0,t,"triggerRun",0,n],86616)}]);
|
|
File without changes
|
|
File without changes
|
|
File without changes
|