@justmpm/firebase-audit 0.5.7 → 0.5.9

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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  scan
3
- } from "./chunk-LJW7QXIJ.js";
3
+ } from "./chunk-B4ATI4OJ.js";
4
4
  import {
5
5
  verify
6
6
  } from "./chunk-CSMT4PIP.js";
@@ -142,17 +142,18 @@ function createMcpServer() {
142
142
  "verify",
143
143
  {
144
144
  title: "Firebase Audit Verify (Emulator)",
145
- description: "Gera matriz papel\xD7opera\xE7\xE3o\xD7path e avalia cobertura do ruleCoverage. Requer Emulator. Sem contrato usa pap\xE9is reduzidos (anonymous/user/admin). Nunca afirma cobertura sem teste real.",
145
+ description: "Gera matriz papel\xD7opera\xE7\xE3o\xD7path e avalia cobertura do ruleCoverage. Requer Emulator. Sem contrato usa pap\xE9is reduzidos (anonymous/user/admin). Nunca afirma cobertura sem teste real. N\xE3o substitui scan/check.",
146
146
  inputSchema: {
147
147
  cwd: z.string().min(1).describe("Diret\xF3rio raiz do projeto"),
148
- coverage: z.string().optional().describe("Path do coverage.json do ruleCoverage (relativo ao cwd)")
148
+ coverage: z.string().optional().describe("Path do coverage.json do ruleCoverage (relativo ao cwd)"),
149
+ adapter: z.string().optional().describe("Fun\xE7\xE3o de permiss\xE3o no c\xF3digo que recebe o nome como texto (ex: rbac.can). Default: do contrato.")
149
150
  }
150
151
  },
151
- async ({ cwd, coverage }) => {
152
+ async ({ cwd, coverage, adapter }) => {
152
153
  try {
153
154
  assertCwd(cwd);
154
155
  const { resolve } = await import("path");
155
- const { summary: scanSummary, model } = await scan(cwd);
156
+ const { summary: scanSummary, model } = await scan(cwd, { adapterFn: adapter });
156
157
  const res = verify(model, { coverageFile: coverage ? resolve(cwd, coverage) : void 0 });
157
158
  const roles = model.authorization.roles.length > 0 ? model.authorization.roles.map((r) => r.name) : ["anonymous", "user", "admin"];
158
159
  const truncated = res.cases.length > 50;
@@ -42,11 +42,12 @@ function stripRuleComments(content) {
42
42
  }
43
43
  function opsFrom(target) {
44
44
  const t = target.trim().toLowerCase();
45
- if (t === "read") return { ops: ["read", "get", "list"], known: true };
46
- if (t === "write") return { ops: ["write", "create", "update", "delete"], known: true };
45
+ if (t === "read") return { ops: ["read", "get", "list"], known: true, unknown: [] };
46
+ if (t === "write") return { ops: ["write", "create", "update", "delete"], known: true, unknown: [] };
47
47
  const parts = t.split(",").map((s) => s.trim()).filter(Boolean);
48
48
  const valid = ["read", "get", "list", "create", "update", "delete", "write"];
49
49
  const out = [];
50
+ const unknown = [];
50
51
  let known = false;
51
52
  for (const p of parts) {
52
53
  if (valid.includes(p)) {
@@ -62,9 +63,11 @@ function opsFrom(target) {
62
63
  if (!out.includes(extra)) out.push(extra);
63
64
  }
64
65
  }
66
+ } else {
67
+ unknown.push(p);
65
68
  }
66
69
  }
67
- return { ops: out, known: out.length > 0 && known };
70
+ return { ops: out, known: out.length > 0 && known, unknown };
68
71
  }
69
72
  function findBlockEnd(content, openBraceIndex) {
70
73
  let depth = 0;
@@ -247,13 +250,14 @@ function extractRulesContent(rawContent, relFile) {
247
250
  const requestResourceReferences = [
248
251
  ...big.matchAll(/request\.resource\.data\.([A-Za-z0-9_]+)/g)
249
252
  ].map((x) => x[1]);
250
- const { ops, known } = opsFrom(target);
253
+ const { ops, known, unknown } = opsFrom(target);
251
254
  const isHelperCall = !unconditional && rawCondition !== resolved;
252
255
  const hasAuth = authSource.includes("request.auth");
253
256
  rules.push({
254
257
  id: `rule-${idx++}`,
255
258
  path: fullPath,
256
259
  operations: ops,
260
+ unknownOperations: unknown.length > 0 ? unknown : void 0,
257
261
  conditionPresent: !unconditional && condition.length > 0,
258
262
  condition: unconditional ? void 0 : condition,
259
263
  authReferences,
@@ -9,7 +9,7 @@ import {
9
9
  resolveHelperConditionDetailed,
10
10
  stripOuterParens,
11
11
  stripRuleComments
12
- } from "./chunk-FECVBQUO.js";
12
+ } from "./chunk-7J3ADEBW.js";
13
13
 
14
14
  // src/scan.ts
15
15
  import { readFileSync as readFileSync3, existsSync as existsSync3, statSync as statSync2 } from "fs";
@@ -107,6 +107,10 @@ var SERVER_HINTS_SEGMENTS = [
107
107
  "backend",
108
108
  "scripts"
109
109
  ];
110
+ function isTestPath(file) {
111
+ const n = file.replace(/\\/g, "/");
112
+ return /\.test\.[tj]sx?$/.test(n) || /\.spec\.[tj]sx?$/.test(n) || /(^|\/)__tests__\//.test(n) || /(^|\/)(test|tests|testing)\//.test(n);
113
+ }
110
114
  function classifyOrigin(file, content) {
111
115
  const rel = file.replace(/\\/g, "/");
112
116
  const segs = rel.split("/");
@@ -189,7 +193,7 @@ import { join as join2 } from "path";
189
193
  import { executeFind } from "@justmpm/supergrep";
190
194
  function isBuildOutput(file) {
191
195
  const n = file.replace(/\\/g, "/");
192
- return /(^|\/)(node_modules|dist|build|\.next|coverage|out)\//.test(n);
196
+ return /(^|\/)(node_modules|dist|build|\.next|coverage|out)\//.test(n) || /\/functions\/lib\//.test(n);
193
197
  }
194
198
  var IMPLEMENTED_CHECKS = ["FBA001", "FBA002", "FBA003", "FBA004", "FBA009", "FBA010", "FBA011", "FBA012", "FBA013"];
195
199
  function createEvidence() {
@@ -276,14 +280,16 @@ async function runChecks(model, rootDir, opts = {}) {
276
280
  }
277
281
  }
278
282
  for (const rule of model.rules) {
279
- if (rule.operations.length === 0) {
283
+ const unknown2 = rule.unknownOperations ?? [];
284
+ if (rule.operations.length === 0 || unknown2.length > 0) {
285
+ const detail = unknown2.length > 0 ? ` (trecho desconhecido: ${unknown2.join(", ")})` : "";
280
286
  findings.push(
281
287
  make(
282
288
  "RULES_UNKNOWN_OP",
283
289
  "WARNING",
284
290
  "CONFIRMED",
285
- `Opera\xE7\xE3o desconhecida em ${rule.path} (${rule.location.file}:${rule.location.start.line}). Typo em allow? Deploy rejeita.`,
286
- `RULES_UNKNOWN_OP:${rule.path}:${rule.location.start.line}`,
291
+ `Opera\xE7\xE3o desconhecida em ${rule.path}${detail} (${rule.location.file}:${rule.location.start.line}). Typo em allow? Deploy rejeita.`,
292
+ `RULES_UNKNOWN_OP:${rule.path}:${rule.location.start.line}:${[...rule.operations].sort().join(",")}`,
287
293
  {
288
294
  file: rule.location.file,
289
295
  line: rule.location.start.line,
@@ -530,10 +536,6 @@ async function runChecks(model, rootDir, opts = {}) {
530
536
  }
531
537
  }
532
538
  const adminHits = await collectAdminImports(rootDir);
533
- const isTestPath = (f) => {
534
- const n = f.replace(/\\/g, "/");
535
- return /\.test\.[tj]sx?$/.test(n) || /\.spec\.[tj]sx?$/.test(n) || /(^|\/)__tests__\//.test(n) || /(^|\/)(test|tests|testing)\//.test(n);
536
- };
537
539
  for (const hit of adminHits) {
538
540
  let origin = "UNKNOWN";
539
541
  try {
@@ -593,7 +595,7 @@ async function collectPermissionCalls(rootDir, adapterFn) {
593
595
  }
594
596
  const seen = /* @__PURE__ */ new Set();
595
597
  const pushLiteral = (file, line, permission) => {
596
- if (isBuildOutput(file)) return;
598
+ if (isBuildOutput(file) || isTestPath(file)) return;
597
599
  const k = `${file}:${line}:${permission}`;
598
600
  if (seen.has(k)) return;
599
601
  seen.add(k);
@@ -606,7 +608,7 @@ async function collectPermissionCalls(rootDir, adapterFn) {
606
608
  const raw = (m.metaVariables["PERM"] ?? "").trim();
607
609
  const line = m.line + 1;
608
610
  if (raw.includes("${")) {
609
- if (!isBuildOutput(m.file)) {
611
+ if (!isBuildOutput(m.file) && !isTestPath(m.file)) {
610
612
  const k = `${m.file}:${line}:${raw}`;
611
613
  if (!seen.has(k)) {
612
614
  seen.add(k);
@@ -620,7 +622,7 @@ async function collectPermissionCalls(rootDir, adapterFn) {
620
622
  const perm = raw.replace(/^['"`]|['"`]$/g, "").trim();
621
623
  if (/^[a-z][a-z0-9_-]*(\.[a-z][a-z0-9_-]*)*$/.test(perm)) pushLiteral(m.file, line, perm);
622
624
  else {
623
- if (!isBuildOutput(m.file)) {
625
+ if (!isBuildOutput(m.file) && !isTestPath(m.file)) {
624
626
  const k = `${m.file}:${line}:${raw}:invalid`;
625
627
  if (!seen.has(k)) {
626
628
  seen.add(k);
@@ -629,7 +631,7 @@ async function collectPermissionCalls(rootDir, adapterFn) {
629
631
  }
630
632
  }
631
633
  } else if (raw.length > 0) {
632
- if (!isBuildOutput(m.file)) {
634
+ if (!isBuildOutput(m.file) && !isTestPath(m.file)) {
633
635
  const k = `${m.file}:${line}:${raw}`;
634
636
  if (!seen.has(k)) {
635
637
  seen.add(k);
@@ -772,6 +774,8 @@ var RuleSchema = z.strictObject({
772
774
  id: z.string().min(1),
773
775
  path: z.string().min(1),
774
776
  operations: z.array(OperationSchema),
777
+ /** Partes de `allow` não reconhecidas (typo parcial: `get, gett`). */
778
+ unknownOperations: z.array(z.string()).optional(),
775
779
  conditionPresent: z.boolean(),
776
780
  condition: z.string().optional(),
777
781
  authReferences: z.array(z.string()),
@@ -908,6 +912,13 @@ function validateClaimSamples(samples) {
908
912
 
909
913
  // src/scan.ts
910
914
  async function scan(rootDir, opts = {}) {
915
+ let rootOk = false;
916
+ try {
917
+ rootOk = statSync2(rootDir).isDirectory();
918
+ } catch {
919
+ rootOk = false;
920
+ }
921
+ if (!rootOk) throw new Error(`Diret\xF3rio n\xE3o existe: ${rootDir}`);
911
922
  const d = discover(rootDir);
912
923
  if (opts.graph === true) {
913
924
  try {
@@ -1421,7 +1432,7 @@ async function countAppCheckEnforced(rootDir, functionsDir) {
1421
1432
  try {
1422
1433
  const { readdirSync, readFileSync: readSync, statSync: stat } = await import("fs");
1423
1434
  const { join: joinP } = await import("path");
1424
- const { stripRuleComments: strip } = await import("./rules-W4VUUQM4.js");
1435
+ const { stripRuleComments: strip } = await import("./rules-HCXATUST.js");
1425
1436
  const roots = [functionsDir ?? joinP(rootDir, "functions")];
1426
1437
  let occurrences = 0;
1427
1438
  const files = /* @__PURE__ */ new Set();
@@ -1430,10 +1441,7 @@ async function countAppCheckEnforced(rootDir, functionsDir) {
1430
1441
  let limitHit = false;
1431
1442
  const stack = [...roots];
1432
1443
  let guard = 0;
1433
- const isTestFile = (p) => {
1434
- const n = p.replace(/\\/g, "/");
1435
- return /\.test\.[tj]sx?$/.test(n) || /\.spec\.[tj]sx?$/.test(n) || /(^|\/)__tests__\//.test(n) || /(^|\/)(test|tests|testing)\//.test(n);
1436
- };
1444
+ const isTestFile = isTestPath;
1437
1445
  while (stack.length > 0 && guard < 200) {
1438
1446
  guard += 1;
1439
1447
  const cur = stack.pop();
@@ -1482,7 +1490,7 @@ async function countAppCheckEnforced(rootDir, functionsDir) {
1482
1490
  files.add(abs);
1483
1491
  }
1484
1492
  }
1485
- const manualHits = text.match(/verifyToken\s*\(|verify_token\s*\(|consumeAppCheckToken|getLimitedUseToken|getAppCheck\s*\(/g);
1493
+ const manualHits = text.match(/verifyToken\s*\(|verify_token\s*\(|consumeAppCheckToken|getLimitedUseToken/g);
1486
1494
  if (manualHits) manual += manualHits.length;
1487
1495
  } catch {
1488
1496
  }
@@ -1588,6 +1596,7 @@ function expandStorageOps(target) {
1588
1596
  export {
1589
1597
  discover,
1590
1598
  SERVER_HINTS_SEGMENTS,
1599
+ isTestPath,
1591
1600
  classifyOrigin,
1592
1601
  emptyModel,
1593
1602
  enrichWithGraph,
package/dist/cli.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  scan
4
- } from "./chunk-LJW7QXIJ.js";
5
- import "./chunk-FECVBQUO.js";
4
+ } from "./chunk-B4ATI4OJ.js";
5
+ import "./chunk-7J3ADEBW.js";
6
6
 
7
7
  // src/cli.ts
8
8
  import { createRequire } from "module";
@@ -46,6 +46,9 @@ 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
+ if (cmd === "check" && strictArg === "false") {
50
+ console.error("Aviso: 'check' \xE9 sempre strict por defini\xE7\xE3o \u2014 --strict=false ignorado.");
51
+ }
49
52
  const asJsonArg = getArg("json");
50
53
  const asJson = (args.includes("--json") || asJsonArg === "true") && asJsonArg !== "false";
51
54
  if (asJsonArg !== void 0 && asJsonArg !== "true" && asJsonArg !== "false") {
@@ -81,7 +84,7 @@ Uso: firebase-audit scan|check|verify|drift [--json] ...`);
81
84
  const { resolve: resolveRoot } = await import("path");
82
85
  const rootDir = cwdArg ? resolveRoot(process.cwd(), cwdArg) : process.cwd();
83
86
  if (cmd === "verify") {
84
- const { scan: scanForVerify } = await import("./scan-A34FDN66.js");
87
+ const { scan: scanForVerify } = await import("./scan-CG54EVVP.js");
85
88
  const { verify } = await import("./verify-GQSR4FQL.js");
86
89
  const { resolve: resolveVerify } = await import("path");
87
90
  const coverageArg = getArg("coverage");
package/dist/index.d.ts CHANGED
@@ -217,6 +217,7 @@ declare const RuleSchema: z.ZodObject<{
217
217
  delete: "delete";
218
218
  write: "write";
219
219
  }>>;
220
+ unknownOperations: z.ZodOptional<z.ZodArray<z.ZodString>>;
220
221
  conditionPresent: z.ZodBoolean;
221
222
  condition: z.ZodOptional<z.ZodString>;
222
223
  authReferences: z.ZodArray<z.ZodString>;
@@ -419,6 +420,7 @@ declare const ProjectModelSchema: z.ZodObject<{
419
420
  delete: "delete";
420
421
  write: "write";
421
422
  }>>;
423
+ unknownOperations: z.ZodOptional<z.ZodArray<z.ZodString>>;
422
424
  conditionPresent: z.ZodBoolean;
423
425
  condition: z.ZodOptional<z.ZodString>;
424
426
  authReferences: z.ZodArray<z.ZodString>;
@@ -639,6 +641,8 @@ interface DiscoveryResult {
639
641
  declare function discover(rootDir: string): DiscoveryResult;
640
642
  /** Segmentos de servidor (fail-closed: `api` genérico NÃO é server, ver classifyOrigin). */
641
643
  declare const SERVER_HINTS_SEGMENTS: string[];
644
+ /** Arquivo de teste/setup de Emulator (com Admin legitimamente): nunca reprova CI. */
645
+ declare function isTestPath(file: string): boolean;
642
646
  /** Heurística CLIENT vs SERVER: caminho manda primeiro (grafo ai-tool preenche listas na Fase 3).
643
647
  * Segmentos ancorados — nunca substring solta (`myapp/` não é `app/`). */
644
648
  declare function classifyOrigin(file: string, content: string): "CLIENT" | "SERVER" | "UNKNOWN";
@@ -665,6 +669,7 @@ declare function stripRuleComments(content: string): string;
665
669
  declare function opsFrom(target: string): {
666
670
  ops: Rule["operations"];
667
671
  known: boolean;
672
+ unknown: string[];
668
673
  };
669
674
  /** Normaliza target de allow para fingerprint estável (case/espaço/ordem). */
670
675
  declare function normalizeTarget(target: string): string;
@@ -763,7 +768,9 @@ interface ScanSummary {
763
768
  errors: number;
764
769
  warnings: number;
765
770
  infos: number;
771
+ /** Checks FBA que passaram (só FBA; infra nomeada vai em `infra`). */
766
772
  passedChecks: number;
773
+ /** Total de checks FBA implementados. */
767
774
  totalChecks: number;
768
775
  /** Infra nomeada (fora dos checks FBA): cada item diz qual e por quê. */
769
776
  infra: {
@@ -845,4 +852,4 @@ declare function drift(localFile: string | null, remoteFile: string | null): Dri
845
852
 
846
853
  declare const VERSION: string;
847
854
 
848
- export { AccessOriginSchema, type AuditYaml, AuditYamlSchema, ConfidenceSchema, type DiscoveryResult, type DriftEntry, type DriftResult, type DriftStatus, DynamicValueSchema, type Evidence, EvidenceSchema, type Finding, FindingSchema, FirestoreIndexSchema, GraphEdgeSchema, IMPLEMENTED_CHECKS, IndexFieldSchema, LocationSchema, OperationSchema, PermissionSchema, PositionSchema, type ProjectModel, ProjectModelSchema, QueryFilterSchema, QueryOrderSchema, type QueryShape, QueryShapeSchema, RESERVED_CLAIM_KEYS, RoleSchema, RuleSchema, SERVER_HINTS_SEGMENTS, type ScanOptions, type ScanResult, type ScanSummary, SeveritySchema, VERSION, type VerifyCase, type VerifyOptions, type VerifyResult, buildFullPath, classifyOrigin, conditionKeyForFingerprint, discover, drift, emptyModel, enrichWithGraph, extractRules, extractRulesContent, findPublicAllows, findPublicAllowsContent, hashShort, helperBodyToCondition, inlineHelperArgs, inlineHelpersInCondition, isPublicCondition, normalizeTarget, opsFrom, parseMatchBlocks, parseRuleFunctions, parseRuleFunctionsDetailed, planVerify, resolveHelperCondition, resolveHelperConditionDetailed, runChecks, scan, stripOuterParens, stripRuleComments, validateClaimSamples, verify };
855
+ export { AccessOriginSchema, type AuditYaml, AuditYamlSchema, ConfidenceSchema, type DiscoveryResult, type DriftEntry, type DriftResult, type DriftStatus, DynamicValueSchema, type Evidence, EvidenceSchema, type Finding, FindingSchema, FirestoreIndexSchema, GraphEdgeSchema, IMPLEMENTED_CHECKS, IndexFieldSchema, LocationSchema, OperationSchema, PermissionSchema, PositionSchema, type ProjectModel, ProjectModelSchema, QueryFilterSchema, QueryOrderSchema, type QueryShape, QueryShapeSchema, RESERVED_CLAIM_KEYS, RoleSchema, RuleSchema, SERVER_HINTS_SEGMENTS, type ScanOptions, type ScanResult, type ScanSummary, SeveritySchema, VERSION, type VerifyCase, type VerifyOptions, type VerifyResult, buildFullPath, classifyOrigin, conditionKeyForFingerprint, discover, drift, emptyModel, enrichWithGraph, extractRules, extractRulesContent, findPublicAllows, findPublicAllowsContent, hashShort, helperBodyToCondition, inlineHelperArgs, inlineHelpersInCondition, isPublicCondition, isTestPath, normalizeTarget, opsFrom, parseMatchBlocks, parseRuleFunctions, parseRuleFunctionsDetailed, planVerify, resolveHelperCondition, resolveHelperConditionDetailed, runChecks, scan, stripOuterParens, stripRuleComments, validateClaimSamples, verify };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createMcpServer,
3
3
  startMcpServer
4
- } from "./chunk-BHRZOZYA.js";
4
+ } from "./chunk-4ST23J6W.js";
5
5
  import {
6
6
  AccessOriginSchema,
7
7
  AuditYamlSchema,
@@ -30,10 +30,11 @@ import {
30
30
  discover,
31
31
  emptyModel,
32
32
  enrichWithGraph,
33
+ isTestPath,
33
34
  runChecks,
34
35
  scan,
35
36
  validateClaimSamples
36
- } from "./chunk-LJW7QXIJ.js";
37
+ } from "./chunk-B4ATI4OJ.js";
37
38
  import {
38
39
  buildFullPath,
39
40
  conditionKeyForFingerprint,
@@ -55,7 +56,7 @@ import {
55
56
  resolveHelperConditionDetailed,
56
57
  stripOuterParens,
57
58
  stripRuleComments
58
- } from "./chunk-FECVBQUO.js";
59
+ } from "./chunk-7J3ADEBW.js";
59
60
  import {
60
61
  planVerify,
61
62
  verify
@@ -111,6 +112,7 @@ export {
111
112
  inlineHelperArgs,
112
113
  inlineHelpersInCondition,
113
114
  isPublicCondition,
115
+ isTestPath,
114
116
  normalizeTarget,
115
117
  opsFrom,
116
118
  parseMatchBlocks,
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-BHRZOZYA.js";
5
- import "./chunk-LJW7QXIJ.js";
6
- import "./chunk-FECVBQUO.js";
4
+ } from "./chunk-4ST23J6W.js";
5
+ import "./chunk-B4ATI4OJ.js";
6
+ import "./chunk-7J3ADEBW.js";
7
7
  import "./chunk-CSMT4PIP.js";
8
8
  import "./chunk-LD3F73XI.js";
9
9
 
package/dist/mcp.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  createMcpServer,
3
3
  startMcpServer
4
- } from "./chunk-BHRZOZYA.js";
5
- import "./chunk-LJW7QXIJ.js";
6
- import "./chunk-FECVBQUO.js";
4
+ } from "./chunk-4ST23J6W.js";
5
+ import "./chunk-B4ATI4OJ.js";
6
+ import "./chunk-7J3ADEBW.js";
7
7
  import "./chunk-CSMT4PIP.js";
8
8
  import "./chunk-LD3F73XI.js";
9
9
  export {
@@ -19,7 +19,7 @@ import {
19
19
  resolveHelperConditionDetailed,
20
20
  stripOuterParens,
21
21
  stripRuleComments
22
- } from "./chunk-FECVBQUO.js";
22
+ } from "./chunk-7J3ADEBW.js";
23
23
  export {
24
24
  buildFullPath,
25
25
  conditionKeyForFingerprint,
@@ -0,0 +1,7 @@
1
+ import {
2
+ scan
3
+ } from "./chunk-B4ATI4OJ.js";
4
+ import "./chunk-7J3ADEBW.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.7",
3
+ "version": "0.5.9",
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",
package/skill/SKILL.md CHANGED
@@ -34,6 +34,6 @@ Use `drift` para comparar índices locais vs implantados (MATCHED/LOCAL_ONLY/REM
34
34
 
35
35
  ## Limites honestos
36
36
 
37
- - AppCheck: varredura TS/Python por texto (`enforceAppCheck`, `verifyToken` e afins). Config via variável conta como não observada; `onRequest` exige revisão rota a rota.
37
+ - AppCheck: varredura TS/Python por texto (`enforceAppCheck`, `verifyToken` e afins). Config via variável ou objeto compartilhado conta como não observada; janela de trigger pode errar em arquivo multi-função — `onRequest` exige revisão rota a rota.
38
38
  - Isolamento por tenant (ownerId/tenantId) e mismatch de papel não-admin: fora dos checks — revise manualmente ou no Emulator.
39
39
  - `graph: true` é prévia opt-in da biblioteca (nenhum check consome ainda).
@@ -1,7 +0,0 @@
1
- import {
2
- scan
3
- } from "./chunk-LJW7QXIJ.js";
4
- import "./chunk-FECVBQUO.js";
5
- export {
6
- scan
7
- };