@justmpm/firebase-audit 0.5.3 → 0.5.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.
@@ -9,7 +9,7 @@ import {
9
9
  resolveHelperConditionDetailed,
10
10
  stripOuterParens,
11
11
  stripRuleComments
12
- } from "./chunk-7EWTBIKJ.js";
12
+ } from "./chunk-FECVBQUO.js";
13
13
 
14
14
  // src/scan.ts
15
15
  import { readFileSync as readFileSync3, existsSync as existsSync3 } from "fs";
@@ -253,7 +253,7 @@ async function runChecks(model, rootDir, opts = {}) {
253
253
  const declared = new Set(model.authorization.permissions.map((p) => p.name));
254
254
  if (permissionCalls.literals.length === 0 && permissionCalls.dynamic.length === 0) {
255
255
  const tenantHint = [...helperDetailed.keys()].find(
256
- (n) => /tenant|isMember|isSignedIn|_member/i.test(n)
256
+ (n) => /tenant|isMember|_member/i.test(n)
257
257
  );
258
258
  findings.push(
259
259
  make(
@@ -367,7 +367,7 @@ async function runChecks(model, rootDir, opts = {}) {
367
367
  const condResolved = resolveHelperConditionDetailed(condRaw, helperDetailed);
368
368
  const condInlined = inlineHelpersInCondition(condRaw, helperDetailed);
369
369
  const cond = condResolved !== condRaw ? condResolved : condInlined !== condRaw ? condInlined : condRaw;
370
- const isInvertedAdmin = /admin\s*==\s*false/i.test(cond) || /admin\s*!=\s*true/i.test(cond) || /admin\s*!==\s*true/i.test(cond) || /!\s*request\.auth\.token\.admin\b/i.test(cond) || /!\s*\(\s*request\.auth\.token\.admin\b/i.test(cond) || /!\s*\(\s*.*admin\s*==\s*true/i.test(cond);
370
+ const isInvertedAdmin = /admin\s*==\s*false/i.test(cond) || /admin\s*!=\s*true/i.test(cond) || /admin\s*!==\s*true/i.test(cond) || /!\s*request\.auth\.token\.admin\b/i.test(cond) || /!\s*\(\s*request\.auth\.token\.admin\b/i.test(cond) || /!\s*\([^()]*admin\s*==\s*true/i.test(cond);
371
371
  const normClaim = (c) => c.toLowerCase().replace(/[_-]/g, "");
372
372
  const isAdminCall = /\bisadmin\s*\(([^)]*)\)/i.exec(condRaw) ?? /\bisadmin\s*\(([^)]*)\)/i.exec(cond);
373
373
  let isAdminBodyHasAdmin = false;
@@ -511,6 +511,22 @@ async function runChecks(model, rootDir, opts = {}) {
511
511
  }
512
512
  )
513
513
  );
514
+ } else if (origin === "UNKNOWN" && !isTestPath(hit.file)) {
515
+ findings.push(
516
+ make(
517
+ "ADMIN_ORIGIN_UNKNOWN",
518
+ "INFO",
519
+ "UNKNOWN",
520
+ `Admin SDK em arquivo de origem indefinida (${hit.file}). Se for cliente, Rules n\xE3o valem \u2014 revise manualmente.`,
521
+ `ADMIN_ORIGIN_UNKNOWN:${hit.file}:${hit.line}`,
522
+ {
523
+ file: hit.file,
524
+ line: hit.line,
525
+ evidence: [ev("admin-import-unknown", hit.text, "UNKNOWN", hit.file, hit.line)],
526
+ fix: "Mova para functions/, server/ ou scripts/ se for server; se for cliente, troque por Client SDK."
527
+ }
528
+ )
529
+ );
514
530
  }
515
531
  }
516
532
  return findings;
@@ -1120,7 +1136,7 @@ async function scan(rootDir, opts = {}) {
1120
1136
  severity: "ERROR",
1121
1137
  confidence: "CONFIRMED",
1122
1138
  message: `Storage com escrita aberta (${where}). Qualquer cliente pode escrever \u2014 cofre aberto, n\xE3o vitrine.`,
1123
- fingerprint: `STORAGE_PUBLIC_WRITE:${m.matchPath}:${m.target}:${key}`,
1139
+ fingerprint: `STORAGE_PUBLIC_WRITE:${m.matchPath}:${normalizeTarget(m.target)}:${key}`,
1124
1140
  file: relStorage,
1125
1141
  line: m.line,
1126
1142
  evidence: [{ id: evId("write"), kind: "storage-public", summary: `${m.matchPath} allow ${m.target}`, confidence: "CONFIRMED", location: { file: relStorage, start: { line: m.line, column: 0 } } }],
@@ -1135,7 +1151,7 @@ async function scan(rootDir, opts = {}) {
1135
1151
  severity: "WARNING",
1136
1152
  confidence: "PROBABLE",
1137
1153
  message: `Storage com leitura aberta em curinga total (${where}). Vale para tudo \u2014 se a inten\xE7\xE3o era s\xF3 logo, restrinja a /logos/{id}.`,
1138
- fingerprint: `STORAGE_ALL_PATHS_OPEN:${m.matchPath}:${m.target}:${key}`,
1154
+ fingerprint: `STORAGE_ALL_PATHS_OPEN:${m.matchPath}:${normalizeTarget(m.target)}:${key}`,
1139
1155
  file: relStorage,
1140
1156
  line: m.line,
1141
1157
  evidence: [{ id: evId("all"), kind: "storage-public", summary: `${m.matchPath} allow ${m.target}`, confidence: "PROBABLE", location: { file: relStorage, start: { line: m.line, column: 0 } } }],
@@ -1148,7 +1164,7 @@ async function scan(rootDir, opts = {}) {
1148
1164
  severity: "INFO",
1149
1165
  confidence: "PROBABLE",
1150
1166
  message: enumerates ? `Storage com leitura aberta (${where}). Pode ser vitrine proposital \u2014 mas allow read/list permite enumerar a pasta; para vitrine segura prefira allow get. Confirme que o write segue restrito.` : `Storage com leitura aberta (${where}). Pode ser vitrine proposital (logos) \u2014 confirme que o write segue restrito.`,
1151
- fingerprint: `STORAGE_PUBLIC_READ:${m.matchPath}:${m.target}:${key}`,
1167
+ fingerprint: `STORAGE_PUBLIC_READ:${m.matchPath}:${normalizeTarget(m.target)}:${key}`,
1152
1168
  file: relStorage,
1153
1169
  line: m.line,
1154
1170
  evidence: [{ id: evId("read"), kind: "storage-public", summary: `${m.matchPath} allow ${m.target}`, confidence: "PROBABLE", location: { file: relStorage, start: { line: m.line, column: 0 } } }],
@@ -1164,7 +1180,7 @@ async function scan(rootDir, opts = {}) {
1164
1180
  severity: "INFO",
1165
1181
  confidence: "PROBABLE",
1166
1182
  message: `Storage aceita qualquer autenticado incluindo an\xF4nimo (${where}). Exija claim/provider para dados sens\xEDveis.`,
1167
- fingerprint: `STORAGE_ANON_ALLOWED:${m.matchPath}:${m.target}:${key}`,
1183
+ fingerprint: `STORAGE_ANON_ALLOWED:${m.matchPath}:${normalizeTarget(m.target)}:${key}`,
1168
1184
  file: relStorage,
1169
1185
  line: m.line,
1170
1186
  evidence: [{ id: evId("anon"), kind: "anon-allowed", summary: `${m.matchPath} allow ${m.target}`, confidence: "PROBABLE", location: { file: relStorage, start: { line: m.line, column: 0 } } }],
@@ -1172,17 +1188,33 @@ async function scan(rootDir, opts = {}) {
1172
1188
  });
1173
1189
  continue;
1174
1190
  }
1175
- if (!hasAuth && m.condition !== void 0 && (ops.has("read") || ops.has("get") || ops.has("list"))) {
1176
- findings.push({
1177
- rule: "STORAGE_PUBLIC_READ",
1178
- severity: "INFO",
1179
- confidence: "PROBABLE",
1180
- message: `Storage com leitura condicionada sem request.auth (${where}). Se a condi\xE7\xE3o for verdadeira para an\xF4nimo, ele l\xEA \u2014 confirme que \xE9 vitrine.`,
1181
- fingerprint: `STORAGE_PUBLIC_READ:${m.matchPath}:${m.target}:${key}`,
1182
- file: relStorage,
1183
- line: m.line,
1184
- evidence: [{ id: evId("read"), kind: "storage-public", summary: `${m.matchPath} allow ${m.target} sem auth`, confidence: "PROBABLE", location: { file: relStorage, start: { line: m.line, column: 0 } } }]
1185
- });
1191
+ if (!hasAuth && m.condition !== void 0) {
1192
+ const canWrite = ops.has("write") || ops.has("create") || ops.has("update") || ops.has("delete");
1193
+ const canRead = ops.has("read") || ops.has("get") || ops.has("list");
1194
+ if (canWrite) {
1195
+ findings.push({
1196
+ rule: "STORAGE_PUBLIC_WRITE",
1197
+ severity: "ERROR",
1198
+ confidence: "PROBABLE",
1199
+ message: `Storage com escrita condicionada sem request.auth (${where}). Se a condi\xE7\xE3o for verdadeira para an\xF4nimo, ele escreve \u2014 cofre prov\xE1vel.`,
1200
+ fingerprint: `STORAGE_PUBLIC_WRITE:${m.matchPath}:${normalizeTarget(m.target)}:${key}`,
1201
+ file: relStorage,
1202
+ line: m.line,
1203
+ evidence: [{ id: evId("write"), kind: "storage-public", summary: `${m.matchPath} allow ${m.target} sem auth`, confidence: "PROBABLE", location: { file: relStorage, start: { line: m.line, column: 0 } } }],
1204
+ fix: "Exigir request.auth (e dono/tenant) em qualquer condi\xE7\xE3o de write."
1205
+ });
1206
+ } else if (canRead) {
1207
+ findings.push({
1208
+ rule: "STORAGE_PUBLIC_READ",
1209
+ severity: "INFO",
1210
+ confidence: "PROBABLE",
1211
+ message: `Storage com leitura condicionada sem request.auth (${where}). Se a condi\xE7\xE3o for verdadeira para an\xF4nimo, ele l\xEA \u2014 confirme que \xE9 vitrine.`,
1212
+ fingerprint: `STORAGE_PUBLIC_READ:${m.matchPath}:${normalizeTarget(m.target)}:${key}`,
1213
+ file: relStorage,
1214
+ line: m.line,
1215
+ evidence: [{ id: evId("read"), kind: "storage-public", summary: `${m.matchPath} allow ${m.target} sem auth`, confidence: "PROBABLE", location: { file: relStorage, start: { line: m.line, column: 0 } } }]
1216
+ });
1217
+ }
1186
1218
  }
1187
1219
  }
1188
1220
  } catch {
@@ -1283,7 +1315,7 @@ async function countAppCheckEnforced(rootDir) {
1283
1315
  try {
1284
1316
  const { readdirSync, readFileSync: readSync, statSync: stat } = await import("fs");
1285
1317
  const { join: joinP } = await import("path");
1286
- const { stripRuleComments: strip } = await import("./rules-GTGZWN5U.js");
1318
+ const { stripRuleComments: strip } = await import("./rules-W4VUUQM4.js");
1287
1319
  const roots = [joinP(rootDir, "functions")];
1288
1320
  let occurrences = 0;
1289
1321
  const files = /* @__PURE__ */ new Set();
@@ -1293,7 +1325,7 @@ async function countAppCheckEnforced(rootDir) {
1293
1325
  let guard = 0;
1294
1326
  const isTestFile = (p) => {
1295
1327
  const n = p.replace(/\\/g, "/");
1296
- return /\.test\.[tj]sx?$/.test(n) || /\.spec\.[tj]sx?$/.test(n) || /(^|\/)__tests__\//.test(n);
1328
+ return /\.test\.[tj]sx?$/.test(n) || /\.spec\.[tj]sx?$/.test(n) || /(^|\/)__tests__\//.test(n) || /(^|\/)(test|tests|testing)\//.test(n);
1297
1329
  };
1298
1330
  while (stack.length > 0 && guard < 200) {
1299
1331
  guard += 1;
@@ -1322,7 +1354,13 @@ async function countAppCheckEnforced(rootDir) {
1322
1354
  if (!/\.(ts|js|mjs|cjs|py)$/.test(name)) continue;
1323
1355
  if (isTestFile(abs)) continue;
1324
1356
  try {
1325
- const text = strip(readSync(abs, "utf-8"));
1357
+ let text = strip(readSync(abs, "utf-8"));
1358
+ if (name.endsWith(".py")) {
1359
+ text = text.split("\n").map((l) => {
1360
+ const idx = l.indexOf("#");
1361
+ return idx === -1 ? l : l.slice(0, idx);
1362
+ }).join("\n");
1363
+ }
1326
1364
  const hits = text.match(/enforceAppCheck\s*:\s*true|enforce_app_check\s*=\s*True/g);
1327
1365
  if (hits) {
1328
1366
  occurrences += hits.length;
@@ -1343,6 +1381,8 @@ function isOpenStorageCondition(condition) {
1343
1381
  if (condition === void 0) return true;
1344
1382
  const norm = stripOuterParens(condition).toLowerCase().replace(/\s+/g, "");
1345
1383
  if (norm === "" || norm === "true") return true;
1384
+ if (/^!\(.*request\.auth(===|==)null/.test(norm)) return false;
1385
+ if (/^!\(.*request\.auth(!==|!=)null/.test(norm)) return true;
1346
1386
  if (/(^|\|\|)true($|\|\|)/.test(norm)) return true;
1347
1387
  if (norm.includes("request.auth==null") || norm.includes("request.auth===null")) return true;
1348
1388
  return false;
@@ -1353,13 +1393,24 @@ function parseStorageAllows(clean) {
1353
1393
  const matchRe = /match\s+/g;
1354
1394
  let mm;
1355
1395
  while ((mm = matchRe.exec(clean)) !== null) {
1356
- const lineStart = clean.lastIndexOf("\n", mm.index) + 1;
1357
- const lineEnd = clean.indexOf("\n", mm.index);
1358
- const line = clean.slice(lineStart, lineEnd === -1 ? void 0 : lineEnd);
1359
- const brace = line.lastIndexOf("{");
1360
- if (brace === -1) continue;
1361
- const path = line.slice(mm.index - lineStart + 5, brace).trim() || "/(unknown)";
1362
- const openBrace = lineStart + brace;
1396
+ const from = mm.index + mm[0].length;
1397
+ let openBrace = -1;
1398
+ let scan2 = from;
1399
+ while (scan2 < clean.length) {
1400
+ const b = clean.indexOf("{", scan2);
1401
+ if (b === -1) break;
1402
+ const between = clean.slice(from, b);
1403
+ if (/match\s|allow\s|;/.test(between)) break;
1404
+ const after = clean.slice(b + 1, b + 12).trimStart().replace(/^\n+/, "");
1405
+ const eol = clean.slice(b + 1).match(/^[ \t]*(\n|$)/) !== null;
1406
+ if (/^(allow|match|function|\})/.test(after) || eol) {
1407
+ openBrace = b;
1408
+ break;
1409
+ }
1410
+ scan2 = b + 1;
1411
+ }
1412
+ if (openBrace === -1) continue;
1413
+ const path = clean.slice(from, openBrace).trim() || "/(unknown)";
1363
1414
  let depth = 0;
1364
1415
  let quote = null;
1365
1416
  let end = clean.length;
@@ -162,21 +162,47 @@ function inlineHelpersInCondition(condition, detailed) {
162
162
  }
163
163
  return out;
164
164
  }
165
+ function isSingleCall(condition) {
166
+ const m = /^([A-Za-z_][A-Za-z0-9_]*)\s*\(/.exec(condition.trim());
167
+ if (!m) return null;
168
+ const open = condition.indexOf("(", m[0].length - 1);
169
+ let depth = 0;
170
+ let quote = null;
171
+ for (let i = open; i < condition.length; i++) {
172
+ const c = condition[i];
173
+ if (quote) {
174
+ if (c === quote && condition[i - 1] !== "\\") quote = null;
175
+ continue;
176
+ }
177
+ if (c === '"' || c === "'" || c === "`") {
178
+ quote = c;
179
+ continue;
180
+ }
181
+ if (c === "(") depth += 1;
182
+ else if (c === ")") {
183
+ depth -= 1;
184
+ if (depth === 0) {
185
+ const rest = condition.slice(i + 1).replace(/;$/, "").trim();
186
+ if (rest !== "") return null;
187
+ return { name: m[1], args: condition.slice(open + 1, i) };
188
+ }
189
+ }
190
+ }
191
+ return null;
192
+ }
165
193
  function resolveHelperCondition(condition, functions) {
166
- const t = condition.trim().replace(/;$/, "").trim();
167
- const call = /^([A-Za-z_][A-Za-z0-9_]*)\s*\((.*)\)$/.exec(t);
168
- if (!call) return condition;
169
- const body = functions.get(call[1]);
194
+ const single = isSingleCall(condition);
195
+ if (!single) return condition;
196
+ const body = functions.get(single.name);
170
197
  if (body === void 0) return condition;
171
198
  return body;
172
199
  }
173
200
  function resolveHelperConditionDetailed(condition, detailed) {
174
- const t = condition.trim().replace(/;$/, "").trim();
175
- const call = /^([A-Za-z_][A-Za-z0-9_]*)\s*\((.*)\)$/.exec(t);
176
- if (!call) return condition;
177
- const fn = detailed.get(call[1]);
201
+ const single = isSingleCall(condition);
202
+ if (!single) return condition;
203
+ const fn = detailed.get(single.name);
178
204
  if (!fn) return condition;
179
- const args = call[2].length === 0 ? [] : call[2].split(",").map((s) => s.trim());
205
+ const args = single.args.length === 0 ? [] : single.args.split(",").map((s) => s.trim());
180
206
  return inlineHelperArgs(fn.body, fn.params, args);
181
207
  }
182
208
  function extractRules(rulesFile, relFile) {
@@ -275,7 +301,10 @@ function isPublicCondition(condition) {
275
301
  return { isPublic: false, confidence: "CONFIRMED" };
276
302
  }
277
303
  if (low === "true") return { isPublic: true, confidence: "CONFIRMED" };
278
- if (/(^|\|\|)true($|\|\|)/.test(low)) return { isPublic: true, confidence: "PROBABLE" };
304
+ for (const part of low.split("||")) {
305
+ const branch = stripOuterParens(part.replace(/return/g, "").replace(/;/g, "").trim());
306
+ if (branch === "true") return { isPublic: true, confidence: "PROBABLE" };
307
+ }
279
308
  if (low.includes("request.auth==null") || low.includes("request.auth===null")) {
280
309
  return { isPublic: true, confidence: "CONFIRMED" };
281
310
  }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  scan
3
- } from "./chunk-4BBFPTOR.js";
3
+ } from "./chunk-AKPQ4OAJ.js";
4
4
  import {
5
5
  verify
6
6
  } from "./chunk-Y4VXWSKX.js";
@@ -201,6 +201,7 @@ function createMcpServer() {
201
201
  try {
202
202
  const { resolve: resolveP, isAbsolute } = await import("path");
203
203
  const base = cwd && cwd.length > 0 ? cwd : process.cwd();
204
+ assertCwd(base);
204
205
  const resPath = (p) => {
205
206
  if (p === null || p === void 0) return null;
206
207
  return isAbsolute(p) ? p : resolveP(base, p);
package/dist/cli.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  scan
4
- } from "./chunk-4BBFPTOR.js";
5
- import "./chunk-7EWTBIKJ.js";
4
+ } from "./chunk-AKPQ4OAJ.js";
5
+ import "./chunk-FECVBQUO.js";
6
6
 
7
7
  // src/cli.ts
8
8
  import { createRequire } from "module";
@@ -46,7 +46,12 @@ Uso: firebase-audit scan [--json] [--strict] [--adapter=fn] [--cwd=path]`);
46
46
  console.error(`Valor inv\xE1lido para --strict: ${strictArg} (use --strict, --strict=true ou --strict=false)`);
47
47
  process.exit(1);
48
48
  }
49
- const asJson = args.includes("--json") || args.includes("--json=true");
49
+ const asJsonArg = getArg("json");
50
+ const asJson = args.includes("--json") || asJsonArg === "true";
51
+ if (asJsonArg !== void 0 && asJsonArg !== "true" && asJsonArg !== "false") {
52
+ console.error(`Valor inv\xE1lido para --json: ${asJsonArg} (use --json, --json=true ou --json=false)`);
53
+ process.exit(1);
54
+ }
50
55
  const knownFlags = /* @__PURE__ */ new Set(["--json", "--strict", "--help", "-h", "help", "--version", "-v", "--coverage", "--local", "--remote"]);
51
56
  for (const a of args.slice(1)) {
52
57
  const base = a.includes("=") ? a.slice(0, a.indexOf("=")) : a;
@@ -59,18 +64,24 @@ Uso: firebase-audit scan|check|verify|drift [--json] ...`);
59
64
  const adapterArg = getArg("adapter");
60
65
  const cwdArg = getArg("cwd");
61
66
  if (cwdArg) {
62
- const { existsSync: existsCwd } = await import("fs");
67
+ const { existsSync: existsCwd, statSync: statCwd } = await import("fs");
63
68
  const { resolve: resolveCwd } = await import("path");
64
69
  const abs = resolveCwd(process.cwd(), cwdArg);
65
- if (!existsCwd(abs)) {
66
- console.error(`Diret\xF3rio --cwd n\xE3o existe: ${cwdArg}`);
70
+ let isDir = false;
71
+ try {
72
+ isDir = existsCwd(abs) && statCwd(abs).isDirectory();
73
+ } catch {
74
+ isDir = false;
75
+ }
76
+ if (!isDir) {
77
+ console.error(`Diret\xF3rio --cwd n\xE3o existe ou n\xE3o \xE9 diret\xF3rio: ${cwdArg}`);
67
78
  process.exit(1);
68
79
  }
69
80
  }
70
81
  const { resolve: resolveRoot } = await import("path");
71
82
  const rootDir = cwdArg ? resolveRoot(process.cwd(), cwdArg) : process.cwd();
72
83
  if (cmd === "verify") {
73
- const { scan: scanForVerify } = await import("./scan-LJ3TCHMR.js");
84
+ const { scan: scanForVerify } = await import("./scan-YUXXYBRU.js");
74
85
  const { verify } = await import("./verify-WBVSESHD.js");
75
86
  const { resolve: resolveVerify } = await import("path");
76
87
  const coverageArg = getArg("coverage");
@@ -169,7 +180,7 @@ ${icon} ${f.rule} [${f.confidence}]${where}
169
180
  if (f.fix) console.log(` \u2192 ${f.fix}`);
170
181
  }
171
182
  console.log(`
172
- SUMMARY: ${summary.errors} errors, ${summary.warnings} warnings, ${summary.infos} infos (${summary.passedChecks}/${summary.totalChecks} checks FBA passaram)`);
183
+ SUMMARY: ${summary.errors} errors, ${summary.warnings} warnings, ${summary.infos} infos (${summary.passedChecks}/${summary.totalChecks} checks FBA passaram${summary.infra.length > 0 ? `, ${summary.infra.length} achado(s) fora dos FBA` : ""})`);
173
184
  console.log(`Olhei: ${summary.checked.join(", ")}`);
174
185
  console.log(`Pulei: ${summary.skipped.join(" | ") || "nada"}
175
186
  `);
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createMcpServer,
3
3
  startMcpServer
4
- } from "./chunk-PWJ4DI24.js";
4
+ } from "./chunk-RKQ3ZWWI.js";
5
5
  import {
6
6
  AccessOriginSchema,
7
7
  AuditYamlSchema,
@@ -33,7 +33,7 @@ import {
33
33
  runChecks,
34
34
  scan,
35
35
  validateClaimSamples
36
- } from "./chunk-4BBFPTOR.js";
36
+ } from "./chunk-AKPQ4OAJ.js";
37
37
  import {
38
38
  buildFullPath,
39
39
  conditionKeyForFingerprint,
@@ -55,7 +55,7 @@ import {
55
55
  resolveHelperConditionDetailed,
56
56
  stripOuterParens,
57
57
  stripRuleComments
58
- } from "./chunk-7EWTBIKJ.js";
58
+ } from "./chunk-FECVBQUO.js";
59
59
  import {
60
60
  planVerify,
61
61
  verify
package/dist/mcp-cli.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startMcpServer
4
- } from "./chunk-PWJ4DI24.js";
5
- import "./chunk-4BBFPTOR.js";
6
- import "./chunk-7EWTBIKJ.js";
4
+ } from "./chunk-RKQ3ZWWI.js";
5
+ import "./chunk-AKPQ4OAJ.js";
6
+ import "./chunk-FECVBQUO.js";
7
7
  import "./chunk-Y4VXWSKX.js";
8
8
  import "./chunk-XZ32YQQC.js";
9
9
 
package/dist/mcp.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  createMcpServer,
3
3
  startMcpServer
4
- } from "./chunk-PWJ4DI24.js";
5
- import "./chunk-4BBFPTOR.js";
6
- import "./chunk-7EWTBIKJ.js";
4
+ } from "./chunk-RKQ3ZWWI.js";
5
+ import "./chunk-AKPQ4OAJ.js";
6
+ import "./chunk-FECVBQUO.js";
7
7
  import "./chunk-Y4VXWSKX.js";
8
8
  import "./chunk-XZ32YQQC.js";
9
9
  export {
@@ -19,7 +19,7 @@ import {
19
19
  resolveHelperConditionDetailed,
20
20
  stripOuterParens,
21
21
  stripRuleComments
22
- } from "./chunk-7EWTBIKJ.js";
22
+ } from "./chunk-FECVBQUO.js";
23
23
  export {
24
24
  buildFullPath,
25
25
  conditionKeyForFingerprint,
@@ -0,0 +1,7 @@
1
+ import {
2
+ scan
3
+ } from "./chunk-AKPQ4OAJ.js";
4
+ import "./chunk-FECVBQUO.js";
5
+ export {
6
+ scan
7
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@justmpm/firebase-audit",
3
- "version": "0.5.3",
3
+ "version": "0.5.4",
4
4
  "description": "Auditor de consistência e segurança para projetos Firebase: código + Rules + índices + contrato vs comportamento. Static-first, nunca inventa certeza.",
5
5
  "keywords": [
6
6
  "firebase",
@@ -1,7 +0,0 @@
1
- import {
2
- scan
3
- } from "./chunk-4BBFPTOR.js";
4
- import "./chunk-7EWTBIKJ.js";
5
- export {
6
- scan
7
- };