@justmpm/firebase-audit 0.4.0 → 0.4.2
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-5PVQB3ZI.js → chunk-3SEVYYCX.js} +9 -4
- package/dist/{chunk-TVEZQU7C.js → chunk-PSSMYRRU.js} +3 -1
- package/dist/{chunk-YVV7YRF3.js → chunk-SKD4TKFU.js} +50 -5
- package/dist/cli.js +12 -3
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -3
- package/dist/mcp-cli.js +3 -3
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +3 -3
- package/dist/{scan-6FRUE5BE.js → scan-VREY2FGO.js} +1 -1
- package/dist/{verify-R6LI2KNZ.js → verify-O722ZZBV.js} +1 -1
- package/package.json +1 -1
|
@@ -783,6 +783,10 @@ async function runChecks(model, rootDir, opts = {}) {
|
|
|
783
783
|
}
|
|
784
784
|
}
|
|
785
785
|
const adminHits = await collectAdminImports(rootDir);
|
|
786
|
+
const isTestPath = (f) => {
|
|
787
|
+
const n = f.replace(/\\/g, "/");
|
|
788
|
+
return /\.test\.[tj]sx?$/.test(n) || /\.spec\.[tj]sx?$/.test(n) || /(^|\/)__tests__\//.test(n) || /(^|\/)(test|tests|testing)\//.test(n);
|
|
789
|
+
};
|
|
786
790
|
for (const hit of adminHits) {
|
|
787
791
|
let origin = "UNKNOWN";
|
|
788
792
|
try {
|
|
@@ -791,7 +795,7 @@ async function runChecks(model, rootDir, opts = {}) {
|
|
|
791
795
|
} catch {
|
|
792
796
|
origin = "UNKNOWN";
|
|
793
797
|
}
|
|
794
|
-
if (origin === "CLIENT") {
|
|
798
|
+
if (origin === "CLIENT" && !isTestPath(hit.file)) {
|
|
795
799
|
findings.push(
|
|
796
800
|
make(
|
|
797
801
|
"FBA003",
|
|
@@ -1329,16 +1333,17 @@ async function scan(rootDir, opts = {}) {
|
|
|
1329
1333
|
});
|
|
1330
1334
|
}
|
|
1331
1335
|
} else {
|
|
1336
|
+
const details = validated.error.issues.slice(0, 5).map((i) => `${String(i.path.join(".")) || "(raiz)"}: ${i.message}`).join("; ");
|
|
1332
1337
|
findings_pre.push({
|
|
1333
1338
|
rule: "CONTRACT_INVALID",
|
|
1334
1339
|
severity: "WARNING",
|
|
1335
1340
|
confidence: "CONFIRMED",
|
|
1336
|
-
message: `firebase-audit.yaml
|
|
1341
|
+
message: `firebase-audit.yaml inv\xE1lido (${validated.error.issues.length} problema(s): ${details}) \u2014 contrato ignorado neste scan.`,
|
|
1337
1342
|
fingerprint: "CONTRACT_INVALID",
|
|
1338
1343
|
evidence: [
|
|
1339
|
-
{ id: "ev-contract-invalid", kind: "contract-parse", summary:
|
|
1344
|
+
{ id: "ev-contract-invalid", kind: "contract-parse", summary: details.slice(0, 200), confidence: "CONFIRMED" }
|
|
1340
1345
|
],
|
|
1341
|
-
fix: "Valide contra templates/firebase-audit.yaml."
|
|
1346
|
+
fix: "Valide contra templates/firebase-audit.yaml (min\xFAsculas, sem chaves extras)."
|
|
1342
1347
|
});
|
|
1343
1348
|
}
|
|
1344
1349
|
} catch (err) {
|
|
@@ -7,7 +7,7 @@ function collectVisitCounts(node, out) {
|
|
|
7
7
|
return;
|
|
8
8
|
}
|
|
9
9
|
const rec = node;
|
|
10
|
-
for (const k of ["visitCount", "visit_count"
|
|
10
|
+
for (const k of ["visitCount", "visit_count"]) {
|
|
11
11
|
const v = rec[k];
|
|
12
12
|
if (typeof v === "number" && Number.isFinite(v)) {
|
|
13
13
|
out.push(v);
|
|
@@ -26,6 +26,7 @@ function planVerify(model) {
|
|
|
26
26
|
};
|
|
27
27
|
const cases = [];
|
|
28
28
|
for (const rule of model.rules) {
|
|
29
|
+
if (rule.path === "/(unknown)") continue;
|
|
29
30
|
for (const role of roles) {
|
|
30
31
|
for (const op of rule.operations) {
|
|
31
32
|
for (const mapped of expand(op)) {
|
|
@@ -105,6 +106,7 @@ function verify(model, opts = {}) {
|
|
|
105
106
|
return { cases, uncovered: [], findings, coveragePct };
|
|
106
107
|
}
|
|
107
108
|
for (const rule of model.rules) {
|
|
109
|
+
if (rule.path === "/(unknown)") continue;
|
|
108
110
|
uncovered.push(rule.path);
|
|
109
111
|
}
|
|
110
112
|
return { cases, uncovered, findings, coveragePct };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
scan
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-3SEVYYCX.js";
|
|
4
4
|
import {
|
|
5
5
|
verify
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-PSSMYRRU.js";
|
|
7
7
|
import {
|
|
8
8
|
drift
|
|
9
9
|
} from "./chunk-NJBCSW7E.js";
|
|
@@ -13,13 +13,58 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
13
13
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
14
14
|
import { z } from "zod";
|
|
15
15
|
import { createRequire } from "module";
|
|
16
|
+
import { readFileSync, existsSync } from "fs";
|
|
17
|
+
import { dirname, join } from "path";
|
|
18
|
+
import { fileURLToPath } from "url";
|
|
16
19
|
var require2 = createRequire(import.meta.url);
|
|
17
20
|
var pkg = require2("../package.json");
|
|
21
|
+
var pkgDir = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
22
|
+
function readPkgText(rel) {
|
|
23
|
+
try {
|
|
24
|
+
const p = join(pkgDir, rel);
|
|
25
|
+
if (!existsSync(p)) return null;
|
|
26
|
+
return readFileSync(p, "utf-8");
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
var FALLBACK_SKILL = [
|
|
32
|
+
"# firebase-audit-skill (fallback)",
|
|
33
|
+
"",
|
|
34
|
+
"1. Descobrir projeto (firebase.json, firestore.rules, firestore.indexes.json).",
|
|
35
|
+
"2. Executar scan (strict no CI). Sem adapter resolvido \u2192 UNKNOWN, nunca adivinhar.",
|
|
36
|
+
"3. CONFIRMED corrige agora; PROBABLE investiga; NOT_OBSERVED/UNKNOWN n\xE3o afirma.",
|
|
37
|
+
"4. Corrigir c\xF3digo, nunca o contrato para esconder finding."
|
|
38
|
+
].join("\n");
|
|
18
39
|
function createMcpServer() {
|
|
19
40
|
const server = new McpServer({
|
|
20
41
|
name: "firebase-audit-mcp-server",
|
|
21
42
|
version: pkg.version
|
|
22
43
|
});
|
|
44
|
+
server.registerTool(
|
|
45
|
+
"firebase_audit_skill",
|
|
46
|
+
{
|
|
47
|
+
title: "Firebase Audit Skill (guia do agente)",
|
|
48
|
+
description: "LEIA ANTES de auditar: sequ\xEAncia obrigat\xF3ria, travas (nunca mude o contrato para esconder finding, nunca afirme sem evid\xEAncia, nunca diga 'unused') e Emulador. Retorna o guia oficial da vers\xE3o instalada + modelo de contrato. Sempre sincronizado \u2014 dispensa skill copiada.",
|
|
49
|
+
inputSchema: {}
|
|
50
|
+
},
|
|
51
|
+
async () => {
|
|
52
|
+
const skill = readPkgText(join("skill", "SKILL.md")) ?? FALLBACK_SKILL;
|
|
53
|
+
const template = readPkgText(join("templates", "firebase-audit.yaml")) ?? "";
|
|
54
|
+
const text = [
|
|
55
|
+
`# firebase-audit v${pkg.version} \u2014 guia do agente`,
|
|
56
|
+
"",
|
|
57
|
+
skill,
|
|
58
|
+
"",
|
|
59
|
+
"## Modelo de contrato (templates/firebase-audit.yaml)",
|
|
60
|
+
"",
|
|
61
|
+
"```yaml",
|
|
62
|
+
template,
|
|
63
|
+
"```"
|
|
64
|
+
].join("\n");
|
|
65
|
+
return { content: [{ type: "text", text }] };
|
|
66
|
+
}
|
|
67
|
+
);
|
|
23
68
|
server.registerTool(
|
|
24
69
|
"firebase_audit_scan",
|
|
25
70
|
{
|
|
@@ -120,8 +165,8 @@ function createMcpServer() {
|
|
|
120
165
|
title: "Firebase Audit Drift (\xEDndices)",
|
|
121
166
|
description: "Compara \xEDndices locais vs implantados. Retorna MATCHED/LOCAL_ONLY/REMOTE_ONLY. Nunca diz 'unused', apenas NOT_OBSERVED.",
|
|
122
167
|
inputSchema: {
|
|
123
|
-
local: z.string().nullable().describe("Path do firestore.indexes.json local (ou null). Relativo resolve contra cwd."),
|
|
124
|
-
remote: z.string().nullable().describe("Path do JSON remoto (gcloud/REST) (ou null). Relativo resolve contra cwd."),
|
|
168
|
+
local: z.string().nullable().optional().describe("Path do firestore.indexes.json local (ou null). Relativo resolve contra cwd."),
|
|
169
|
+
remote: z.string().nullable().optional().describe("Path do JSON remoto (gcloud/REST) (ou null). Relativo resolve contra cwd."),
|
|
125
170
|
cwd: z.string().optional().describe("Diret\xF3rio base para paths relativos (default: cwd do servidor).")
|
|
126
171
|
}
|
|
127
172
|
},
|
|
@@ -130,7 +175,7 @@ function createMcpServer() {
|
|
|
130
175
|
const { resolve: resolveP, isAbsolute } = await import("path");
|
|
131
176
|
const base = cwd && cwd.length > 0 ? cwd : process.cwd();
|
|
132
177
|
const resPath = (p) => {
|
|
133
|
-
if (p === null) return null;
|
|
178
|
+
if (p === null || p === void 0) return null;
|
|
134
179
|
return isAbsolute(p) ? p : resolveP(base, p);
|
|
135
180
|
};
|
|
136
181
|
const res = drift(resPath(local), resPath(remote));
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
scan
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-3SEVYYCX.js";
|
|
5
5
|
|
|
6
6
|
// src/cli.ts
|
|
7
7
|
import { createRequire } from "module";
|
|
@@ -64,8 +64,8 @@ Uso: firebase-audit scan|check|verify|drift [--json] ...`);
|
|
|
64
64
|
const { resolve: resolveRoot } = await import("path");
|
|
65
65
|
const rootDir = cwdArg ? resolveRoot(process.cwd(), cwdArg) : process.cwd();
|
|
66
66
|
if (cmd === "verify") {
|
|
67
|
-
const { scan: scanForVerify } = await import("./scan-
|
|
68
|
-
const { verify } = await import("./verify-
|
|
67
|
+
const { scan: scanForVerify } = await import("./scan-VREY2FGO.js");
|
|
68
|
+
const { verify } = await import("./verify-O722ZZBV.js");
|
|
69
69
|
const { resolve: resolveVerify } = await import("path");
|
|
70
70
|
const coverageArg = getArg("coverage");
|
|
71
71
|
const coveragePath = coverageArg ? resolveVerify(rootDir, coverageArg) : void 0;
|
|
@@ -107,6 +107,15 @@ ${icon} ${f.rule} [${f.confidence}]
|
|
|
107
107
|
const remotePath = remoteArg ? resolveDrift(rootDir, remoteArg) : null;
|
|
108
108
|
for (const [label, p] of [["--local", localPath], ["--remote", remotePath]]) {
|
|
109
109
|
if (p && !existsDrift(p)) {
|
|
110
|
+
if (asJson) {
|
|
111
|
+
const { drift: driftMissing } = await import("./drift-KU7HWL7M.js");
|
|
112
|
+
const res2 = driftMissing(
|
|
113
|
+
localPath && existsDrift(localPath) ? localPath : null,
|
|
114
|
+
remotePath && existsDrift(remotePath) ? remotePath : null
|
|
115
|
+
);
|
|
116
|
+
console.log(JSON.stringify({ version: pkg.version, entries: res2.entries, findings: res2.findings }, null, 2));
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
110
119
|
console.error(`Arquivo ${label} n\xE3o encontrado: ${p}`);
|
|
111
120
|
process.exit(1);
|
|
112
121
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -643,6 +643,10 @@ declare function enrichWithGraph(rootDir: string, d: DiscoveryResult): Promise<v
|
|
|
643
643
|
/**
|
|
644
644
|
* Rules extractor — estrutura + referências + locations (sem avaliar).
|
|
645
645
|
* Parser leve e intencionalmente incompleto: avaliação real é do Emulator (V3).
|
|
646
|
+
*
|
|
647
|
+
* Limitação conhecida: `ALLOW_RE` corta a condição no primeiro `;` mesmo dentro
|
|
648
|
+
* de string literal (ex: `x != "a;b"`). Raríssimo em Rules reais; documentado aqui
|
|
649
|
+
* em vez de silêncio — Emulator acusa a regra de verdade.
|
|
646
650
|
*/
|
|
647
651
|
|
|
648
652
|
type Rule = z$1.infer<typeof RuleSchema>;
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createMcpServer,
|
|
3
3
|
startMcpServer
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-SKD4TKFU.js";
|
|
5
5
|
import {
|
|
6
6
|
AccessOriginSchema,
|
|
7
7
|
AuditYamlSchema,
|
|
@@ -53,11 +53,11 @@ import {
|
|
|
53
53
|
stripOuterParens,
|
|
54
54
|
stripRuleComments,
|
|
55
55
|
validateClaimSamples
|
|
56
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-3SEVYYCX.js";
|
|
57
57
|
import {
|
|
58
58
|
planVerify,
|
|
59
59
|
verify
|
|
60
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-PSSMYRRU.js";
|
|
61
61
|
import {
|
|
62
62
|
drift
|
|
63
63
|
} from "./chunk-NJBCSW7E.js";
|
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-SKD4TKFU.js";
|
|
5
|
+
import "./chunk-3SEVYYCX.js";
|
|
6
|
+
import "./chunk-PSSMYRRU.js";
|
|
7
7
|
import "./chunk-NJBCSW7E.js";
|
|
8
8
|
|
|
9
9
|
// src/mcp-cli.ts
|
package/dist/mcp.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
|
3
3
|
/**
|
|
4
4
|
* MCP Server do firebase-audit.
|
|
5
5
|
*
|
|
6
|
-
* Expõe scan/check/verify/drift via protocolo MCP (stdio).
|
|
6
|
+
* Expõe skill + scan/check/verify/drift via protocolo MCP (stdio).
|
|
7
7
|
* Mesma lógica da biblioteca — sem duplicação (chama scan/verify/drift direto).
|
|
8
8
|
*
|
|
9
9
|
* Uso:
|
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-SKD4TKFU.js";
|
|
5
|
+
import "./chunk-3SEVYYCX.js";
|
|
6
|
+
import "./chunk-PSSMYRRU.js";
|
|
7
7
|
import "./chunk-NJBCSW7E.js";
|
|
8
8
|
export {
|
|
9
9
|
createMcpServer,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@justmpm/firebase-audit",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
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",
|