brakit 0.9.1 → 0.10.0

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.
@@ -73,7 +73,7 @@ var init_type_guards = __esm({
73
73
  });
74
74
 
75
75
  // src/constants/labels.ts
76
- var DASHBOARD_PREFIX, DASHBOARD_API_REQUESTS, DASHBOARD_API_EVENTS, DASHBOARD_API_FLOWS, DASHBOARD_API_CLEAR, DASHBOARD_API_LOGS, DASHBOARD_API_FETCHES, DASHBOARD_API_ERRORS, DASHBOARD_API_QUERIES, DASHBOARD_API_INGEST, DASHBOARD_API_METRICS, DASHBOARD_API_ACTIVITY, DASHBOARD_API_METRICS_LIVE, DASHBOARD_API_INSIGHTS, DASHBOARD_API_SECURITY, DASHBOARD_API_TAB, DASHBOARD_API_FINDINGS, DASHBOARD_API_FINDINGS_REPORT, VALID_TABS_TUPLE, VALID_TABS, POSTHOG_HOST, POSTHOG_CAPTURE_PATH, POSTHOG_REQUEST_TIMEOUT_MS;
76
+ var DASHBOARD_PREFIX, DASHBOARD_API_REQUESTS, DASHBOARD_API_EVENTS, DASHBOARD_API_FLOWS, DASHBOARD_API_CLEAR, DASHBOARD_API_LOGS, DASHBOARD_API_FETCHES, DASHBOARD_API_ERRORS, DASHBOARD_API_QUERIES, DASHBOARD_API_INGEST, DASHBOARD_API_METRICS, DASHBOARD_API_ACTIVITY, DASHBOARD_API_METRICS_LIVE, DASHBOARD_API_INSIGHTS, DASHBOARD_API_SECURITY, DASHBOARD_API_TAB, DASHBOARD_API_FINDINGS, DASHBOARD_API_FINDINGS_REPORT, DASHBOARD_API_GRAPH, VALID_TABS_TUPLE, VALID_TABS, POSTHOG_HOST, POSTHOG_CAPTURE_PATH, POSTHOG_REQUEST_TIMEOUT_MS;
77
77
  var init_labels = __esm({
78
78
  "src/constants/labels.ts"() {
79
79
  "use strict";
@@ -95,6 +95,7 @@ var init_labels = __esm({
95
95
  DASHBOARD_API_TAB = `${DASHBOARD_PREFIX}/api/tab`;
96
96
  DASHBOARD_API_FINDINGS = `${DASHBOARD_PREFIX}/api/findings`;
97
97
  DASHBOARD_API_FINDINGS_REPORT = `${DASHBOARD_PREFIX}/api/findings/report`;
98
+ DASHBOARD_API_GRAPH = `${DASHBOARD_PREFIX}/api/graph`;
98
99
  VALID_TABS_TUPLE = [
99
100
  "overview",
100
101
  "actions",
@@ -104,7 +105,8 @@ var init_labels = __esm({
104
105
  "errors",
105
106
  "logs",
106
107
  "performance",
107
- "security"
108
+ "security",
109
+ "graph"
108
110
  ];
109
111
  VALID_TABS = new Set(VALID_TABS_TUPLE);
110
112
  POSTHOG_HOST = "https://us.i.posthog.com";
@@ -138,7 +140,7 @@ var init_features = __esm({
138
140
  MAX_TIMELINE_EVENTS = 20;
139
141
  MAX_RESOLVED_DISPLAY = 5;
140
142
  ENRICHMENT_SEVERITY_FILTER = ["critical", "warning"];
141
- MCP_SERVER_VERSION = "0.9.1";
143
+ MCP_SERVER_VERSION = "0.10.0";
142
144
  RECOVERY_WINDOW_MS = 5 * 60 * 1e3;
143
145
  PORT_MIN = 1;
144
146
  PORT_MAX = 65535;
@@ -1063,7 +1065,7 @@ import { defineCommand } from "citty";
1063
1065
  import { resolve as resolve3, join as join3, dirname } from "path";
1064
1066
  import { readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
1065
1067
  import { execSync } from "child_process";
1066
- import { existsSync as existsSync5 } from "fs";
1068
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
1067
1069
  import pc from "picocolors";
1068
1070
 
1069
1071
  // src/store/issue-store.ts
@@ -1207,7 +1209,8 @@ async function detectPythonFramework(rootDir, hasPyproject, hasRequirements) {
1207
1209
  const content = await readFile3(join2(rootDir, "requirements.txt"), "utf-8");
1208
1210
  const lines = content.toLowerCase().split("\n");
1209
1211
  for (const [dep, fw] of Object.entries(PYTHON_FRAMEWORK_MAP)) {
1210
- if (lines.some((l) => l.startsWith(dep) && (l.length === dep.length || /[=<>~![]/u.test(l[dep.length])))) {
1212
+ const versionChars = /* @__PURE__ */ new Set(["=", "<", ">", "~", "!", "["]);
1213
+ if (lines.some((l) => l.startsWith(dep) && (l.length === dep.length || versionChars.has(l[dep.length])))) {
1211
1214
  return fw;
1212
1215
  }
1213
1216
  }
@@ -1313,20 +1316,115 @@ init_log();
1313
1316
  init_type_guards();
1314
1317
 
1315
1318
  // src/analysis/rules/patterns.ts
1316
- var SECRET_KEYS = /^(password|passwd|secret|api_key|apiKey|api_secret|apiSecret|private_key|privateKey|client_secret|clientSecret)$/;
1317
- var TOKEN_PARAMS = /^(token|api_key|apiKey|secret|password|access_token|session_id|sessionId)$/;
1318
- var SAFE_PARAMS = /^(_rsc|__clerk_handshake|__clerk_db_jwt|callback|code|state|nonce|redirect_uri|utm_|fbclid|gclid)$/;
1319
+ var SECRET_KEY_SET = /* @__PURE__ */ new Set([
1320
+ "password",
1321
+ "passwd",
1322
+ "secret",
1323
+ "api_key",
1324
+ "apiKey",
1325
+ "api_secret",
1326
+ "apiSecret",
1327
+ "private_key",
1328
+ "privateKey",
1329
+ "client_secret",
1330
+ "clientSecret"
1331
+ ]);
1332
+ var SECRET_KEYS = { test: (s) => SECRET_KEY_SET.has(s) };
1333
+ var TOKEN_PARAM_SET = /* @__PURE__ */ new Set([
1334
+ "token",
1335
+ "api_key",
1336
+ "apiKey",
1337
+ "secret",
1338
+ "password",
1339
+ "access_token",
1340
+ "session_id",
1341
+ "sessionId"
1342
+ ]);
1343
+ var TOKEN_PARAMS = { test: (s) => TOKEN_PARAM_SET.has(s) };
1344
+ var SAFE_PARAM_SET = /* @__PURE__ */ new Set([
1345
+ "_rsc",
1346
+ "__clerk_handshake",
1347
+ "__clerk_db_jwt",
1348
+ "callback",
1349
+ "code",
1350
+ "state",
1351
+ "nonce",
1352
+ "redirect_uri",
1353
+ "utm_",
1354
+ "fbclid",
1355
+ "gclid"
1356
+ ]);
1357
+ var SAFE_PARAMS = { test: (s) => SAFE_PARAM_SET.has(s) };
1358
+ var INTERNAL_ID_KEY_SET = /* @__PURE__ */ new Set([
1359
+ "id",
1360
+ "_id",
1361
+ "userId",
1362
+ "user_id",
1363
+ "createdBy",
1364
+ "updatedBy",
1365
+ "organizationId",
1366
+ "org_id",
1367
+ "tenantId",
1368
+ "tenant_id"
1369
+ ]);
1370
+ var INTERNAL_ID_KEYS = { test: (s) => INTERNAL_ID_KEY_SET.has(s) };
1371
+ var INTERNAL_ID_SUFFIX = {
1372
+ test: (s) => s.endsWith("Id") || s.endsWith("_id")
1373
+ };
1374
+ var SENSITIVE_FIELD_SET = /* @__PURE__ */ new Set([
1375
+ "phone",
1376
+ "phonenumber",
1377
+ "phone_number",
1378
+ "ssn",
1379
+ "socialsecuritynumber",
1380
+ "social_security_number",
1381
+ "dateofbirth",
1382
+ "date_of_birth",
1383
+ "dob",
1384
+ "address",
1385
+ "streetaddress",
1386
+ "street_address",
1387
+ "creditcard",
1388
+ "credit_card",
1389
+ "cardnumber",
1390
+ "card_number",
1391
+ "bankaccount",
1392
+ "bank_account",
1393
+ "passport",
1394
+ "passportnumber",
1395
+ "passport_number",
1396
+ "nationalid",
1397
+ "national_id"
1398
+ ]);
1399
+ var SENSITIVE_FIELD_NAMES = {
1400
+ test: (s) => SENSITIVE_FIELD_SET.has(s.toLowerCase())
1401
+ };
1402
+ var SELF_SERVICE_SEGMENTS = /* @__PURE__ */ new Set(["me", "account", "profile", "settings", "self"]);
1403
+ var SELF_SERVICE_PATH = {
1404
+ test: (path) => {
1405
+ const segments = path.toLowerCase().split(/[/?#]/);
1406
+ return segments.some((seg) => SELF_SERVICE_SEGMENTS.has(seg));
1407
+ }
1408
+ };
1409
+ var MASKED_LITERALS = ["[REDACTED]", "[FILTERED]", "CHANGE_ME"];
1410
+ var MASKED_RE = {
1411
+ test: (s) => {
1412
+ const upper = s.toUpperCase();
1413
+ if (MASKED_LITERALS.some((m) => upper.includes(m))) return true;
1414
+ if (s.length > 0 && s.split("").every((c) => c === "*")) return true;
1415
+ if (s.length >= 3 && s.split("").every((c) => c === "x" || c === "X")) return true;
1416
+ return false;
1417
+ }
1418
+ };
1419
+ var DB_PROTOCOLS = ["postgres://", "mysql://", "mongodb://", "redis://"];
1420
+ var DB_CONN_RE = {
1421
+ test: (s) => DB_PROTOCOLS.some((p) => s.includes(p))
1422
+ };
1319
1423
  var STACK_TRACE_RE = /at\s+.+\(.+:\d+:\d+\)|at\s+Module\._compile|at\s+Object\.<anonymous>|at\s+processTicksAndRejections|Traceback \(most recent call last\)|File ".+", line \d+/;
1320
- var DB_CONN_RE = /(postgres|mysql|mongodb|redis):\/\//;
1321
1424
  var SQL_FRAGMENT_RE = /\b(SELECT\s+[\w.*]+\s+FROM|INSERT\s+INTO|UPDATE\s+\w+\s+SET|DELETE\s+FROM)\b/i;
1322
1425
  var SECRET_VAL_RE = /(api_key|apiKey|secret|token)\s*[:=]\s*["']?[A-Za-z0-9_\-.+/]{8,}/;
1323
1426
  var LOG_SECRET_RE = /(password|secret|token|api_key|apiKey)\s*[:=]\s*["']?[A-Za-z0-9_\-.+/]{8,}/i;
1324
- var MASKED_RE = /^\*+$|\[REDACTED\]|\[FILTERED\]|CHANGE_ME|^x{3,}$/i;
1325
1427
  var EMAIL_RE = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/;
1326
- var INTERNAL_ID_KEYS = /^(id|_id|userId|user_id|createdBy|updatedBy|organizationId|org_id|tenantId|tenant_id)$/;
1327
- var INTERNAL_ID_SUFFIX = /Id$|_id$/;
1328
- var SELF_SERVICE_PATH = /\/(?:me|account|profile|settings|self)(?=\/|\?|#|$)/i;
1329
- var SENSITIVE_FIELD_NAMES = /^(phone|phoneNumber|phone_number|ssn|socialSecurityNumber|social_security_number|dateOfBirth|date_of_birth|dob|address|streetAddress|street_address|creditCard|credit_card|cardNumber|card_number|bankAccount|bank_account|passport|passportNumber|passport_number|nationalId|national_id)$/i;
1330
1428
  var RULE_HINTS = {
1331
1429
  "exposed-secret": "Never include secret fields in API responses. Strip sensitive fields before returning.",
1332
1430
  "token-in-url": "Pass tokens in the Authorization header, not URL query parameters.",
@@ -1812,7 +1910,7 @@ init_constants();
1812
1910
  init_endpoint();
1813
1911
 
1814
1912
  // src/index.ts
1815
- var VERSION = "0.9.1";
1913
+ var VERSION = "0.10.0";
1816
1914
 
1817
1915
  // src/cli/commands/install.ts
1818
1916
  init_constants();
@@ -1915,6 +2013,14 @@ var install_default = defineCommand({
1915
2013
  }
1916
2014
  process.exit(1);
1917
2015
  }
2016
+ const firstNode = nodeProjects[0];
2017
+ if (isFrontendOnly(firstNode.dir)) {
2018
+ console.log(pc.yellow(" \u26A0 This looks like a frontend-only app (React, Vue, etc.)."));
2019
+ console.log(pc.dim(" Brakit instruments your backend server, not the browser."));
2020
+ console.log(pc.dim(" Install brakit in your backend project instead (Express, Fastify, Next.js, etc.)."));
2021
+ console.log();
2022
+ process.exit(1);
2023
+ }
1918
2024
  for (const p of nodeProjects) {
1919
2025
  const node = p.node;
1920
2026
  const suffix = p.relDir === "." ? "" : ` in ${p.relDir}`;
@@ -1951,9 +2057,8 @@ var install_default = defineCommand({
1951
2057
  }
1952
2058
  }
1953
2059
  console.log();
1954
- const port = nodeProjects[0].node?.defaultPort ?? 3e3;
1955
2060
  console.log(pc.dim(" Start your app and visit:"));
1956
- console.log(pc.bold(` http://localhost:${port}/__brakit`));
2061
+ console.log(pc.bold(" http://localhost:<your-port>/__brakit"));
1957
2062
  if (pythonProjects.length > 0) {
1958
2063
  const pyLabel = pythonProjects.map((p) => p.relDir).join(", ");
1959
2064
  console.log();
@@ -2105,6 +2210,20 @@ function findGitRoot(startDir) {
2105
2210
  dir = parent;
2106
2211
  }
2107
2212
  }
2213
+ var FRONTEND_ONLY_DEPS = ["react", "vue", "svelte", "@angular/core", "solid-js", "preact"];
2214
+ var SERVER_DEPS = ["express", "fastify", "hono", "koa", "nest", "next", "@remix-run/dev", "nuxt", "astro"];
2215
+ function isFrontendOnly(rootDir) {
2216
+ try {
2217
+ const raw = readFileSync3(join3(rootDir, "package.json"), "utf-8");
2218
+ const pkg = JSON.parse(raw);
2219
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
2220
+ const hasFrontend = FRONTEND_ONLY_DEPS.some((d) => d in allDeps);
2221
+ const hasServer = SERVER_DEPS.some((d) => d in allDeps);
2222
+ return hasFrontend && !hasServer;
2223
+ } catch {
2224
+ return false;
2225
+ }
2226
+ }
2108
2227
  function printManualInstructions(framework) {
2109
2228
  console.log(pc.yellow(" \u26A0 Could not auto-detect entry file."));
2110
2229
  console.log();
@@ -2140,7 +2259,7 @@ import { spawn } from "child_process";
2140
2259
  init_features();
2141
2260
  import { homedir as homedir2, platform } from "os";
2142
2261
  import { join as join4 } from "path";
2143
- import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
2262
+ import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
2144
2263
  import { randomUUID as randomUUID2 } from "crypto";
2145
2264
  var IS_WINDOWS = platform() === "win32";
2146
2265
  var CONFIG_DIR = join4(homedir2(), ".brakit");
@@ -2148,7 +2267,7 @@ var CONFIG_PATH = join4(CONFIG_DIR, "config.json");
2148
2267
  function readConfig() {
2149
2268
  try {
2150
2269
  if (!existsSync6(CONFIG_PATH)) return null;
2151
- return JSON.parse(readFileSync3(CONFIG_PATH, "utf-8"));
2270
+ return JSON.parse(readFileSync4(CONFIG_PATH, "utf-8"));
2152
2271
  } catch {
2153
2272
  return null;
2154
2273
  }