@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.
- package/dist/{chunk-4BBFPTOR.js → chunk-AKPQ4OAJ.js} +79 -28
- package/dist/{chunk-7EWTBIKJ.js → chunk-FECVBQUO.js} +39 -10
- package/dist/{chunk-PWJ4DI24.js → chunk-RKQ3ZWWI.js} +2 -1
- package/dist/cli.js +19 -8
- package/dist/index.js +3 -3
- package/dist/mcp-cli.js +3 -3
- package/dist/mcp.js +3 -3
- package/dist/{rules-GTGZWN5U.js → rules-W4VUUQM4.js} +1 -1
- package/dist/scan-YUXXYBRU.js +7 -0
- package/package.json +1 -1
- package/dist/scan-LJ3TCHMR.js +0 -7
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
resolveHelperConditionDetailed,
|
|
10
10
|
stripOuterParens,
|
|
11
11
|
stripRuleComments
|
|
12
|
-
} from "./chunk-
|
|
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|
|
|
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*\(
|
|
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
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
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-
|
|
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
|
-
|
|
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
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
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
|
|
167
|
-
|
|
168
|
-
|
|
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
|
|
175
|
-
|
|
176
|
-
|
|
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 =
|
|
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
|
-
|
|
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-
|
|
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-
|
|
5
|
-
import "./chunk-
|
|
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
|
|
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
|
-
|
|
66
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
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-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
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 {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@justmpm/firebase-audit",
|
|
3
|
-
"version": "0.5.
|
|
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",
|