@ttt-productions/repo-guards 0.1.16 → 0.1.18
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/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/modules/backend/auth.d.ts +28 -0
- package/dist/modules/backend/auth.d.ts.map +1 -1
- package/dist/modules/backend/auth.js +238 -2
- package/dist/modules/backend/auth.js.map +1 -1
- package/dist/modules/backend/functions-build.d.ts.map +1 -1
- package/dist/modules/backend/functions-build.js +2 -1
- package/dist/modules/backend/functions-build.js.map +1 -1
- package/dist/modules/backend/guards.d.ts +2 -1
- package/dist/modules/backend/guards.d.ts.map +1 -1
- package/dist/modules/backend/guards.js +2 -1
- package/dist/modules/backend/guards.js.map +1 -1
- package/dist/modules/core-definitions/app-routes.d.ts +20 -0
- package/dist/modules/core-definitions/app-routes.d.ts.map +1 -0
- package/dist/modules/core-definitions/app-routes.js +242 -0
- package/dist/modules/core-definitions/app-routes.js.map +1 -0
- package/dist/modules/core-definitions/guards.d.ts +2 -0
- package/dist/modules/core-definitions/guards.d.ts.map +1 -1
- package/dist/modules/core-definitions/guards.js +2 -0
- package/dist/modules/core-definitions/guards.js.map +1 -1
- package/dist/modules/firestore/analyzer.d.ts.map +1 -1
- package/dist/modules/firestore/analyzer.js +30 -2
- package/dist/modules/firestore/analyzer.js.map +1 -1
- package/dist/modules/firestore/guard.d.ts +1 -1
- package/dist/modules/firestore/guard.d.ts.map +1 -1
- package/dist/modules/firestore/guard.js +2 -1
- package/dist/modules/firestore/guard.js.map +1 -1
- package/dist/modules/frontend-actions/callables.d.ts.map +1 -1
- package/dist/modules/frontend-actions/callables.js +2 -1
- package/dist/modules/frontend-actions/callables.js.map +1 -1
- package/dist/modules/frontend-actions/guards.d.ts +2 -0
- package/dist/modules/frontend-actions/guards.d.ts.map +1 -1
- package/dist/modules/frontend-actions/guards.js +2 -0
- package/dist/modules/frontend-actions/guards.js.map +1 -1
- package/dist/modules/frontend-actions/mutations.d.ts +14 -0
- package/dist/modules/frontend-actions/mutations.d.ts.map +1 -0
- package/dist/modules/frontend-actions/mutations.js +86 -0
- package/dist/modules/frontend-actions/mutations.js.map +1 -0
- package/dist/modules/frontend-actions/raw-mutations.d.ts +4 -0
- package/dist/modules/frontend-actions/raw-mutations.d.ts.map +1 -0
- package/dist/modules/frontend-actions/raw-mutations.js +93 -0
- package/dist/modules/frontend-actions/raw-mutations.js.map +1 -0
- package/dist/modules/frontend-reads/guards.d.ts +2 -0
- package/dist/modules/frontend-reads/guards.d.ts.map +1 -1
- package/dist/modules/frontend-reads/guards.js +2 -0
- package/dist/modules/frontend-reads/guards.js.map +1 -1
- package/dist/modules/frontend-reads/read-state-branches.d.ts +31 -0
- package/dist/modules/frontend-reads/read-state-branches.d.ts.map +1 -0
- package/dist/modules/frontend-reads/read-state-branches.js +389 -0
- package/dist/modules/frontend-reads/read-state-branches.js.map +1 -0
- package/dist/modules/import-paths/guard.js +2 -2
- package/dist/modules/import-paths/guard.js.map +1 -1
- package/dist/modules/repo/document-drift.js +1 -1
- package/dist/modules/repo/document-drift.js.map +1 -1
- package/dist/modules/repo/guards.d.ts +1 -1
- package/dist/modules/repo/guards.d.ts.map +1 -1
- package/dist/modules/repo/guards.js.map +1 -1
- package/dist/modules/ui/guards.d.ts +2 -0
- package/dist/modules/ui/guards.d.ts.map +1 -1
- package/dist/modules/ui/guards.js +2 -0
- package/dist/modules/ui/guards.js.map +1 -1
- package/dist/modules/ui/truncation.d.ts +24 -0
- package/dist/modules/ui/truncation.d.ts.map +1 -0
- package/dist/modules/ui/truncation.js +187 -0
- package/dist/modules/ui/truncation.js.map +1 -0
- package/dist/node/layout.d.ts +3 -1
- package/dist/node/layout.d.ts.map +1 -1
- package/dist/node/layout.js +8 -0
- package/dist/node/layout.js.map +1 -1
- package/dist/node/reader.d.ts.map +1 -1
- package/dist/node/reader.js +3 -3
- package/dist/node/reader.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { declaredLiteral, matchPattern, ownerOnly, reviewedAllowlist, sampleHits, type AllowlistEntry, type Hit, type Literal, type Matcher, } from './shapes/shapes.js';
|
|
2
2
|
export { formatFindings, type Finding, type Guard, type GuardContext, type Sample, } from './guard.js';
|
|
3
3
|
export { viewOfText, type FileView } from './reader/view.js';
|
|
4
|
+
export { rawMutations } from './modules/frontend-actions/raw-mutations.js';
|
|
4
5
|
export { MODULE_LOADS, type ImportedName, type ImportKind, type ModuleImport, } from './support/imports.js';
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,YAAY,EACZ,SAAS,EACT,iBAAiB,EACjB,UAAU,EACV,KAAK,cAAc,EACnB,KAAK,GAAG,EACR,KAAK,OAAO,EACZ,KAAK,OAAO,GACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,cAAc,EACd,KAAK,OAAO,EACZ,KAAK,KAAK,EACV,KAAK,YAAY,EACjB,KAAK,MAAM,GACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EACL,YAAY,EACZ,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,YAAY,GAClB,MAAM,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,YAAY,EACZ,SAAS,EACT,iBAAiB,EACjB,UAAU,EACV,KAAK,cAAc,EACnB,KAAK,GAAG,EACR,KAAK,OAAO,EACZ,KAAK,OAAO,GACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,cAAc,EACd,KAAK,OAAO,EACZ,KAAK,KAAK,EACV,KAAK,YAAY,EACjB,KAAK,MAAM,GACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,6CAA6C,CAAC;AAC3E,OAAO,EACL,YAAY,EACZ,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,YAAY,GAClB,MAAM,sBAAsB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { declaredLiteral, matchPattern, ownerOnly, reviewedAllowlist, sampleHits, } from './shapes/shapes.js';
|
|
2
2
|
export { formatFindings, } from './guard.js';
|
|
3
3
|
export { viewOfText } from './reader/view.js';
|
|
4
|
+
export { rawMutations } from './modules/frontend-actions/raw-mutations.js';
|
|
4
5
|
export { MODULE_LOADS, } from './support/imports.js';
|
|
5
6
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,YAAY,EACZ,SAAS,EACT,iBAAiB,EACjB,UAAU,GAKX,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,cAAc,GAKf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAiB,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EACL,YAAY,GAIb,MAAM,sBAAsB,CAAC","sourcesContent":["export {\n declaredLiteral,\n matchPattern,\n ownerOnly,\n reviewedAllowlist,\n sampleHits,\n type AllowlistEntry,\n type Hit,\n type Literal,\n type Matcher,\n} from './shapes/shapes.js';\nexport {\n formatFindings,\n type Finding,\n type Guard,\n type GuardContext,\n type Sample,\n} from './guard.js';\nexport { viewOfText, type FileView } from './reader/view.js';\nexport {\n MODULE_LOADS,\n type ImportedName,\n type ImportKind,\n type ModuleImport,\n} from './support/imports.js';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,YAAY,EACZ,SAAS,EACT,iBAAiB,EACjB,UAAU,GAKX,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,cAAc,GAKf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAiB,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,6CAA6C,CAAC;AAC3E,OAAO,EACL,YAAY,GAIb,MAAM,sBAAsB,CAAC","sourcesContent":["export {\n declaredLiteral,\n matchPattern,\n ownerOnly,\n reviewedAllowlist,\n sampleHits,\n type AllowlistEntry,\n type Hit,\n type Literal,\n type Matcher,\n} from './shapes/shapes.js';\nexport {\n formatFindings,\n type Finding,\n type Guard,\n type GuardContext,\n type Sample,\n} from './guard.js';\nexport { viewOfText, type FileView } from './reader/view.js';\nexport { rawMutations } from './modules/frontend-actions/raw-mutations.js';\nexport {\n MODULE_LOADS,\n type ImportedName,\n type ImportKind,\n type ModuleImport,\n} from './support/imports.js';\n"]}
|
|
@@ -46,5 +46,33 @@ interface Callable {
|
|
|
46
46
|
export declare function callablesOf(file: FileView): Callable[];
|
|
47
47
|
export declare function asksForAdmin(file: FileView, adminAuth: AppCheckFacts['adminAuth']): boolean;
|
|
48
48
|
export declare const appCheckGuard: GenericGuardDefinition<AppCheckFacts>;
|
|
49
|
+
export interface AdminActionSurfaceFacts {
|
|
50
|
+
adminAuth: {
|
|
51
|
+
call: string;
|
|
52
|
+
option: string;
|
|
53
|
+
};
|
|
54
|
+
adminCheckers: readonly string[];
|
|
55
|
+
checkerReaders: readonly Reviewed[];
|
|
56
|
+
adminDeclarations: CallableDeclarationFacts;
|
|
57
|
+
claimKeys: readonly string[];
|
|
58
|
+
adminHooks: readonly string[];
|
|
59
|
+
adminAreaRoots: readonly string[];
|
|
60
|
+
affordanceOwner: string;
|
|
61
|
+
reviewed: ReadonlyArray<{
|
|
62
|
+
path: string;
|
|
63
|
+
symbol?: string;
|
|
64
|
+
why: string;
|
|
65
|
+
}>;
|
|
66
|
+
importAliases: Readonly<Record<string, string>>;
|
|
67
|
+
appServerZone: ServerZone;
|
|
68
|
+
}
|
|
69
|
+
export declare function adminOnlyCallables(file: FileView, adminAuth: AdminActionSurfaceFacts['adminAuth']): string[];
|
|
70
|
+
interface ClientAdminUse {
|
|
71
|
+
index: number;
|
|
72
|
+
symbol: string;
|
|
73
|
+
detail: string;
|
|
74
|
+
}
|
|
75
|
+
export declare function clientAdminUses(file: FileView, facts: Pick<AdminActionSurfaceFacts, 'claimKeys' | 'adminHooks'>, declarationModuleOf: (spec: string) => string | null): ClientAdminUse[];
|
|
76
|
+
export declare const adminActionSurfaceGuard: GenericGuardDefinition<AdminActionSurfaceFacts>;
|
|
49
77
|
export {};
|
|
50
78
|
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/modules/backend/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAGzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAA0C,KAAK,OAAO,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/modules/backend/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
import { ownerOnly, reviewedAllowlist } from '../../shapes/shapes.js';
|
|
3
3
|
import { calleeName, forEachNode, lineOfNode } from '../../support/ast.js';
|
|
4
|
-
import { namesProblem, reviewedProblems, takesNoFacts, textProblem } from '../../support/facts.js';
|
|
4
|
+
import { isText, namesProblem, objectProblem, pathProblem, reviewedProblems, takesNoFacts, textProblem, } from '../../support/facts.js';
|
|
5
5
|
import { MODULE_LOADS, moduleImports } from '../../support/imports.js';
|
|
6
|
+
import { aliasList, resolveSpecifier } from '../../support/resolve-specifier.js';
|
|
6
7
|
import { escapeRegExp, isUnder } from '../../support/text.js';
|
|
7
8
|
import { declarationFactProblems, readDeclarations, } from '../frontend-actions/declarations.js';
|
|
8
|
-
import { serverZoneContext, serverZoneProblems, staleServerFolders, } from './shared.js';
|
|
9
|
+
import { inServerZone, serverZoneContext, serverZoneProblems, staleServerFolders, } from './shared.js';
|
|
9
10
|
const FUNCTIONS_CODE = { roots: ['functions'], kinds: ['code'], tests: false };
|
|
10
11
|
const CLAIM_RECEIVER = String.raw `(?:[\w$]*[Tt]oken|[\w$]*[Cc]laims|decoded[\w$]*)`;
|
|
11
12
|
export function adminClaimMatcher(claimKeys) {
|
|
@@ -391,4 +392,239 @@ export const appCheckGuard = {
|
|
|
391
392
|
return [posture, client];
|
|
392
393
|
},
|
|
393
394
|
};
|
|
395
|
+
const APP_CLIENT_CODE = { roots: ['app'], kinds: ['code'], tests: false };
|
|
396
|
+
function authCallsIn(root, adminAuth) {
|
|
397
|
+
const calls = [];
|
|
398
|
+
forEachNode(root, (node) => {
|
|
399
|
+
if (!ts.isCallExpression(node) || calleeName(node) !== adminAuth.call)
|
|
400
|
+
return;
|
|
401
|
+
calls.push({
|
|
402
|
+
admin: node.arguments.some((arg) => ts.isObjectLiteralExpression(arg) &&
|
|
403
|
+
arg.properties.some((p) => p.name?.getText().replace(/^['"]|['"]$/g, '') === adminAuth.option &&
|
|
404
|
+
!(ts.isPropertyAssignment(p) && isOffLiteral(p.initializer)))),
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
return calls;
|
|
408
|
+
}
|
|
409
|
+
const isOffLiteral = (value) => value.kind === ts.SyntaxKind.FalseKeyword ||
|
|
410
|
+
value.kind === ts.SyntaxKind.NullKeyword ||
|
|
411
|
+
(ts.isIdentifier(value) && value.text === 'undefined');
|
|
412
|
+
export function adminOnlyCallables(file, adminAuth) {
|
|
413
|
+
const moduleCalls = authCallsIn(file.sourceFile, adminAuth);
|
|
414
|
+
return callablesOf(file)
|
|
415
|
+
.filter((callable) => {
|
|
416
|
+
const own = authCallsIn(callable.node, adminAuth);
|
|
417
|
+
const calls = own.length ? own : moduleCalls;
|
|
418
|
+
return calls.length > 0 && calls.every((c) => c.admin);
|
|
419
|
+
})
|
|
420
|
+
.map((callable) => callable.name);
|
|
421
|
+
}
|
|
422
|
+
export function clientAdminUses(file, facts, declarationModuleOf) {
|
|
423
|
+
const out = [];
|
|
424
|
+
const keys = new RegExp(`\\b(${facts.claimKeys.map(escapeRegExp).join('|')})\\b`);
|
|
425
|
+
for (const hit of adminClaimMatcher(facts.claimKeys)(file))
|
|
426
|
+
out.push({
|
|
427
|
+
index: hit.index,
|
|
428
|
+
symbol: keys.exec(hit.detail ?? '')?.[1] ?? '',
|
|
429
|
+
detail: `reads the admin claim (${hit.detail})`,
|
|
430
|
+
});
|
|
431
|
+
const hooks = new Set(facts.adminHooks);
|
|
432
|
+
for (const site of moduleImports(file.sourceFile, MODULE_LOADS)) {
|
|
433
|
+
const index = site.node.getStart(file.sourceFile);
|
|
434
|
+
const module = declarationModuleOf(site.spec);
|
|
435
|
+
if (module) {
|
|
436
|
+
const names = site.named.map((n) => n.imported);
|
|
437
|
+
for (const symbol of names.length ? names : ['*'])
|
|
438
|
+
out.push({
|
|
439
|
+
index,
|
|
440
|
+
symbol,
|
|
441
|
+
detail: `imports ${symbol} from the admin declarations ${module}`,
|
|
442
|
+
});
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
for (const name of site.named)
|
|
446
|
+
if (hooks.has(name.imported))
|
|
447
|
+
out.push({
|
|
448
|
+
index,
|
|
449
|
+
symbol: name.imported,
|
|
450
|
+
detail: `imports the admin hook ${name.imported}`,
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
return out.sort((a, b) => a.index - b.index);
|
|
454
|
+
}
|
|
455
|
+
function adminAreaProblems(facts, problems) {
|
|
456
|
+
if (!Array.isArray(facts?.adminAreaRoots) || !facts.adminAreaRoots.length)
|
|
457
|
+
problems.push('adminAreaRoots is missing');
|
|
458
|
+
else
|
|
459
|
+
facts.adminAreaRoots.forEach((r, i) => pathProblem(r, `adminAreaRoots[${i}]`, problems));
|
|
460
|
+
pathProblem(facts?.affordanceOwner, 'affordanceOwner', problems);
|
|
461
|
+
reviewedProblems(facts?.reviewed, 'reviewed', problems, {
|
|
462
|
+
id: (e) => typeof e.path === 'string' ? `${e.path}${isText(e.symbol) ? ` ${e.symbol}` : ''}` : undefined,
|
|
463
|
+
extra(entry, label) {
|
|
464
|
+
if (entry.symbol !== undefined && !isText(entry.symbol))
|
|
465
|
+
problems.push(`${label}'s symbol is empty`);
|
|
466
|
+
},
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
export const adminActionSurfaceGuard = {
|
|
470
|
+
id: 'adminActionSurface',
|
|
471
|
+
absentWhen: 'the app has no admin actions',
|
|
472
|
+
factProblems(facts) {
|
|
473
|
+
const problems = [];
|
|
474
|
+
if (objectProblem(facts?.adminAuth, 'adminAuth', problems)) {
|
|
475
|
+
textProblem(facts.adminAuth.call, 'adminAuth.call', problems);
|
|
476
|
+
textProblem(facts.adminAuth.option, 'adminAuth.option', problems);
|
|
477
|
+
}
|
|
478
|
+
namesProblem(facts?.adminCheckers, 'adminCheckers', problems);
|
|
479
|
+
reviewedProblems(facts?.checkerReaders, 'checkerReaders', problems);
|
|
480
|
+
declarationFactProblems(facts?.adminDeclarations, 'adminDeclarations', problems);
|
|
481
|
+
namesProblem(facts?.claimKeys, 'claimKeys', problems);
|
|
482
|
+
namesProblem(facts?.adminHooks, 'adminHooks', problems, { nonEmpty: false });
|
|
483
|
+
adminAreaProblems(facts, problems);
|
|
484
|
+
objectProblem(facts?.importAliases, 'importAliases', problems);
|
|
485
|
+
serverZoneProblems(facts?.appServerZone, 'appServerZone', problems);
|
|
486
|
+
return problems;
|
|
487
|
+
},
|
|
488
|
+
build(facts) {
|
|
489
|
+
const declarationModules = new Set(facts.adminDeclarations.modules);
|
|
490
|
+
const probePath = `${facts.affordanceOwner.slice(0, facts.affordanceOwner.lastIndexOf('/') + 1)}probe.tsx`;
|
|
491
|
+
return [
|
|
492
|
+
{
|
|
493
|
+
title: 'admin actions outside the admin area go through the admin-actions affordance',
|
|
494
|
+
description: `The admin-only callables are every callable whose ${facts.adminAuth.call} calls all carry ` +
|
|
495
|
+
`${facts.adminAuth.option}, read from the Functions source each run. Inside a callable module, admin standing ` +
|
|
496
|
+
`is checked only through that auth call: ${facts.adminCheckers.join(', ')} is called only by the reviewed ` +
|
|
497
|
+
`readers. ${facts.adminDeclarations.modules.join(', ')} declares exactly the admin-only callables, no more and ` +
|
|
498
|
+
`no fewer. Outside the admin area (${facts.adminAreaRoots.join(', ')}) and the admin-actions affordance ` +
|
|
499
|
+
`(${facts.affordanceOwner}), client code never reads the admin claim, imports an admin declaration, or imports ` +
|
|
500
|
+
'an admin hook: an admin action there goes through the affordance, and any other admin-standing read is a ' +
|
|
501
|
+
'reviewed entry ({ path, symbol?, why }). A reviewed reader or entry that no longer matches is stale.',
|
|
502
|
+
samples: {
|
|
503
|
+
flags: [
|
|
504
|
+
{ path: probePath, text: `if (claims?.${facts.claimKeys[0]}) show();` },
|
|
505
|
+
...facts.adminHooks
|
|
506
|
+
.slice(0, 1)
|
|
507
|
+
.map((hook) => ({ path: probePath, text: `import { ${hook} } from './hooks';` })),
|
|
508
|
+
],
|
|
509
|
+
passes: [
|
|
510
|
+
{ path: probePath, text: `const label = '${facts.claimKeys[0]}';` },
|
|
511
|
+
{ path: probePath, text: "import { useRows } from './hooks';" },
|
|
512
|
+
],
|
|
513
|
+
probe: (view) => clientAdminUses(view, facts, () => null),
|
|
514
|
+
},
|
|
515
|
+
run(context) {
|
|
516
|
+
const findings = [];
|
|
517
|
+
const adminCallables = new Map();
|
|
518
|
+
const readers = new Map(facts.checkerReaders.map((r) => [r.path, false]));
|
|
519
|
+
const checkers = new Set(facts.adminCheckers);
|
|
520
|
+
for (const file of context.files(FUNCTIONS_CODE)) {
|
|
521
|
+
const callables = callablesOf(file);
|
|
522
|
+
if (!callables.length)
|
|
523
|
+
continue;
|
|
524
|
+
for (const name of adminOnlyCallables(file, facts.adminAuth)) {
|
|
525
|
+
const callable = callables.find((c) => c.name === name);
|
|
526
|
+
adminCallables.set(name, { file: file.path, line: lineOfNode(file, callable.node) });
|
|
527
|
+
}
|
|
528
|
+
forEachNode(file.sourceFile, (node) => {
|
|
529
|
+
if (!ts.isCallExpression(node))
|
|
530
|
+
return;
|
|
531
|
+
const name = calleeName(node);
|
|
532
|
+
if (!name || !checkers.has(name))
|
|
533
|
+
return;
|
|
534
|
+
if (readers.has(file.path))
|
|
535
|
+
readers.set(file.path, true);
|
|
536
|
+
else
|
|
537
|
+
findings.push({
|
|
538
|
+
file: file.path,
|
|
539
|
+
line: lineOfNode(file, node),
|
|
540
|
+
detail: `a callable module checks admin standing through ${name} instead of its ${facts.adminAuth.call}`,
|
|
541
|
+
});
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
for (const [path, used] of readers)
|
|
545
|
+
if (!used)
|
|
546
|
+
findings.push({
|
|
547
|
+
file: path,
|
|
548
|
+
detail: 'stale reader: it no longer calls an admin checker in a callable module',
|
|
549
|
+
});
|
|
550
|
+
if (!adminCallables.size)
|
|
551
|
+
findings.push({
|
|
552
|
+
file: '(functions)',
|
|
553
|
+
detail: `no callable asks ${facts.adminAuth.call} for ${facts.adminAuth.option}, so no admin-only callable was found`,
|
|
554
|
+
});
|
|
555
|
+
const { declared, findings: declarationFindings } = readDeclarations(context, facts.adminDeclarations);
|
|
556
|
+
findings.push(...declarationFindings);
|
|
557
|
+
const declaredNames = new Set();
|
|
558
|
+
for (const d of declared) {
|
|
559
|
+
if (d.callable === null)
|
|
560
|
+
continue;
|
|
561
|
+
declaredNames.add(d.callable);
|
|
562
|
+
if (!adminCallables.has(d.callable))
|
|
563
|
+
findings.push({
|
|
564
|
+
file: d.file,
|
|
565
|
+
line: d.line,
|
|
566
|
+
detail: `${d.hook} declares ${d.callable}, which is not an admin-only callable`,
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
for (const [name, where] of adminCallables)
|
|
570
|
+
if (!declaredNames.has(name))
|
|
571
|
+
findings.push({
|
|
572
|
+
...where,
|
|
573
|
+
detail: `${name} is admin-only, but ${facts.adminDeclarations.modules.join(', ')} does not declare it`,
|
|
574
|
+
});
|
|
575
|
+
for (const root of facts.adminAreaRoots)
|
|
576
|
+
if (!context.reader.exists(root))
|
|
577
|
+
findings.push({ file: root, detail: 'stale admin-area root: it no longer exists' });
|
|
578
|
+
const ownerExists = context.reader.exists(facts.affordanceOwner);
|
|
579
|
+
if (!ownerExists)
|
|
580
|
+
findings.push({
|
|
581
|
+
file: facts.affordanceOwner,
|
|
582
|
+
detail: 'the admin-actions affordance no longer exists',
|
|
583
|
+
});
|
|
584
|
+
const aliases = aliasList(facts.importAliases);
|
|
585
|
+
const reviewedKey = (e) => `${e.path}${e.symbol ? ` ${e.symbol}` : ''}`;
|
|
586
|
+
const reviewedSeen = new Set();
|
|
587
|
+
let ownerHolds = false;
|
|
588
|
+
for (const file of context.files(APP_CLIENT_CODE)) {
|
|
589
|
+
if (inServerZone(file, facts.appServerZone))
|
|
590
|
+
continue;
|
|
591
|
+
if (facts.adminAreaRoots.some((root) => isUnder(file.path, root)))
|
|
592
|
+
continue;
|
|
593
|
+
const uses = clientAdminUses(file, facts, (spec) => {
|
|
594
|
+
const resolved = resolveSpecifier(context.reader, file.path, spec, aliases);
|
|
595
|
+
return declarationModules.has(resolved) ? resolved : null;
|
|
596
|
+
});
|
|
597
|
+
if (file.path === facts.affordanceOwner) {
|
|
598
|
+
ownerHolds ||= uses.length > 0;
|
|
599
|
+
continue;
|
|
600
|
+
}
|
|
601
|
+
for (const use of uses) {
|
|
602
|
+
const entry = facts.reviewed.find((e) => e.path === file.path && (e.symbol === undefined || e.symbol === use.symbol));
|
|
603
|
+
if (entry)
|
|
604
|
+
reviewedSeen.add(reviewedKey(entry));
|
|
605
|
+
else
|
|
606
|
+
findings.push({
|
|
607
|
+
file: file.path,
|
|
608
|
+
line: file.lineOf(use.index),
|
|
609
|
+
detail: `outside the admin area: ${use.detail}`,
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
if (ownerExists && !ownerHolds)
|
|
614
|
+
findings.push({
|
|
615
|
+
file: facts.affordanceOwner,
|
|
616
|
+
detail: 'stale affordance owner: it reads no admin standing and imports no admin declaration or hook',
|
|
617
|
+
});
|
|
618
|
+
for (const entry of facts.reviewed)
|
|
619
|
+
if (!reviewedSeen.has(reviewedKey(entry)))
|
|
620
|
+
findings.push({
|
|
621
|
+
file: entry.path,
|
|
622
|
+
detail: `stale entry: it no longer reads admin standing${entry.symbol ? ` through ${entry.symbol}` : ''}`,
|
|
623
|
+
});
|
|
624
|
+
return findings;
|
|
625
|
+
},
|
|
626
|
+
},
|
|
627
|
+
];
|
|
628
|
+
},
|
|
629
|
+
};
|
|
394
630
|
//# sourceMappingURL=auth.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/modules/backend/auth.ts"],"names":[],"mappings":"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","sourcesContent":["import ts from 'typescript';\nimport type { GenericGuardDefinition } from '../../node/suite/config.js';\nimport type { Finding, Guard, GuardContext } from '../../guard.js';\nimport type { Scope } from '../../node/reader.js';\nimport type { Reviewed } from '../../node/layout.js';\nimport type { FileView } from '../../reader/view.js';\nimport { ownerOnly, reviewedAllowlist, type Hit, type Matcher } from '../../shapes/shapes.js';\nimport { calleeName, forEachNode, lineOfNode } from '../../support/ast.js';\nimport { namesProblem, reviewedProblems, takesNoFacts, textProblem } from '../../support/facts.js';\nimport { MODULE_LOADS, moduleImports } from '../../support/imports.js';\nimport { escapeRegExp, isUnder } from '../../support/text.js';\nimport {\n declarationFactProblems,\n readDeclarations,\n type CallableDeclarationFacts,\n} from '../frontend-actions/declarations.js';\nimport {\n serverZoneContext,\n serverZoneProblems,\n staleServerFolders,\n type ServerZone,\n} from './shared.js';\n\nconst FUNCTIONS_CODE: Scope = { roots: ['functions'], kinds: ['code'], tests: false };\n\nexport interface AdminClaimFacts {\n claimKeys: readonly string[];\n readers: readonly Reviewed[];\n appServerZone: ServerZone;\n}\n\nconst CLAIM_RECEIVER = String.raw`(?:[\\w$]*[Tt]oken|[\\w$]*[Cc]laims|decoded[\\w$]*)`;\n\nexport function adminClaimMatcher(claimKeys: readonly string[]): Matcher {\n const keys = claimKeys.map(escapeRegExp).join('|');\n const patterns = [\n new RegExp(String.raw`\\b${CLAIM_RECEIVER}\\s*(?:\\?\\.|\\.)\\s*(?:${keys})\\b`, 'g'),\n new RegExp(String.raw`\\b${CLAIM_RECEIVER}\\s*(?:\\?\\.)?\\s*\\[\\s*(['\"\\`])(?:${keys})\\1\\s*\\]`, 'g'),\n new RegExp(\n String.raw`\\{[^{}\\n]*\\b(?:${keys})\\b[^{}\\n]*\\}\\s*=\\s*[^;\\n]*${CLAIM_RECEIVER}\\b`,\n 'g',\n ),\n ];\n return (file) => {\n const hits: Hit[] = [];\n for (const re of patterns) {\n const g = new RegExp(re.source, re.flags);\n for (let m = g.exec(file.code); m; m = g.exec(file.code))\n hits.push({ index: m.index, detail: m[0].trim().slice(0, 80) });\n }\n return hits.sort((a, b) => a.index - b.index);\n };\n}\n\nexport const adminClaimGuard: GenericGuardDefinition<AdminClaimFacts> = {\n id: 'adminClaimRead',\n absentWhen: 'the app has no admin claim',\n factProblems(facts) {\n const problems: string[] = [];\n namesProblem(facts?.claimKeys, 'claimKeys', problems);\n reviewedProblems(facts?.readers, 'readers', problems, { nonEmpty: true });\n serverZoneProblems(facts?.appServerZone, 'appServerZone', problems);\n return problems;\n },\n build(facts) {\n const guard = reviewedAllowlist({\n title: 'the admin claim is read only by its resolver',\n description:\n `No server module reads an admin claim (${facts.claimKeys.join(', ')}) off a token or claims payload — by member ` +\n 'access, bracket access, or destructuring — outside the reviewed readers. The claim is metadata and stays true ' +\n 'for up to an hour after a demotion, and anything signed from it outlives the revocation by its own lifetime. ' +\n \"Resolve admin standing through the resolver's authoritative read, failing closed on a read failure. Writing the \" +\n 'claim is not a read. A reviewed reader that no longer reads the claim is stale.',\n scope: {\n roots: ['functions', 'app'],\n optionalRoots: ['functions'],\n kinds: ['code'],\n tests: false,\n },\n match: adminClaimMatcher(facts.claimKeys),\n allowlist: facts.readers,\n samples: {\n flags: [\n `if (request.auth.token.${facts.claimKeys[0]} === true) return all;`,\n `const a = decoded?.['${facts.claimKeys[0]}'];`,\n `const { uid, ${facts.claimKeys[0]} = false } = ctx.token;`,\n ],\n passes: [\n `await auth.setCustomUserClaims(uid, { ${facts.claimKeys[0]}: true });\\nif (args.${facts.claimKeys[0]}) run();`,\n ],\n },\n });\n return [\n {\n ...guard,\n async run(context: GuardContext) {\n return [\n ...staleServerFolders(context, facts.appServerZone),\n ...(await guard.run(serverZoneContext(context, facts.appServerZone))),\n ];\n },\n },\n ];\n },\n};\n\nexport interface RequestOriginFacts {\n reader: string;\n}\n\nconst ORIGIN_READ = /\\brawRequest\\b|x-forwarded-for|\\breq\\s*\\.\\s*ip\\b/gi;\n\nexport const requestOriginGuard: GenericGuardDefinition<RequestOriginFacts> = {\n id: 'requestOrigin',\n absentWhen: false,\n factProblems(facts) {\n const problems: string[] = [];\n textProblem(facts?.reader, 'reader', problems);\n return problems;\n },\n build(facts) {\n return [\n ownerOnly({\n title: \"a request's origin is read in one place\",\n description: `A request's origin is read only in ${facts.reader}, which must still hold the read.`,\n scope: FUNCTIONS_CODE,\n match: (file) => {\n const re = new RegExp(ORIGIN_READ.source, ORIGIN_READ.flags);\n const hits: Hit[] = [];\n for (let m = re.exec(file.code); m; m = re.exec(file.code))\n hits.push({ index: m.index, detail: m[0] });\n return hits;\n },\n owners: [facts.reader],\n samples: {\n flags: [\n 'const ip = request.rawRequest?.ip ?? null;',\n \"const hops = headers['x-forwarded-for'];\",\n 'const caller = req.ip;',\n 'const { rawRequest } = request;',\n ],\n passes: ['const facts = { ip: context.ip };\\n// rawRequest is read only by the reader'],\n },\n }),\n ];\n },\n};\n\nexport type NoBlockingFunctionFacts = Record<string, never>;\n\nconst BLOCKING_MODULE = /^firebase-functions\\/(?:v2\\/)?identity$/;\nconst BLOCKING_BUILDERS = new Set(['beforeCreate', 'beforeSignIn', 'beforeEmail', 'beforeSms']);\n\nexport function blockingFunctionSites(file: FileView): Array<{ node: ts.Node; detail: string }> {\n const out: Array<{ node: ts.Node; detail: string }> = [];\n for (const { spec, node } of moduleImports(file.sourceFile, MODULE_LOADS))\n if (BLOCKING_MODULE.test(spec)) out.push({ node, detail: `imports ${spec}` });\n forEachNode(file.sourceFile, (node) => {\n if (\n ts.isCallExpression(node) &&\n ts.isPropertyAccessExpression(node.expression) &&\n BLOCKING_BUILDERS.has(node.expression.name.text)\n ) {\n out.push({ node, detail: `registers ${node.expression.name.text}` });\n }\n });\n return out;\n}\n\nexport const noBlockingFunctionGuard: GenericGuardDefinition<NoBlockingFunctionFacts> = {\n id: 'noAuthBlockingFunction',\n absentWhen: false,\n factProblems: takesNoFacts,\n build() {\n return [\n {\n title: 'no sign-in waits on a blocking function',\n description:\n 'No Functions source registers an Identity Platform blocking function (the identity module, or a v1 ' +\n 'beforeCreate / beforeSignIn / beforeEmail / beforeSms builder). Identity Platform refuses a sign-up or ' +\n 'sign-in whose blocking function has not answered within seconds, and a cold instance can take longer than ' +\n 'that to start, so a blocking function refuses members at random. Do the work after the fact instead.',\n run(context) {\n return context.files(FUNCTIONS_CODE).flatMap((file) =>\n blockingFunctionSites(file).map(({ node, detail }) => ({\n file: file.path,\n line: lineOfNode(file, node),\n detail,\n })),\n );\n },\n },\n ];\n },\n};\n\nexport interface AppCheckFacts {\n consumingFolders: readonly string[];\n adminAuth: { call: string; option: string };\n consumptionExempt: ReadonlyArray<{ callable: string; why: string }>;\n clientDeclarations: CallableDeclarationFacts;\n noClientDeclaration: ReadonlyArray<{ callable: string; why: string }>;\n}\n\ninterface Callable {\n name: string;\n node: ts.Node;\n options: ts.ObjectLiteralExpression | null;\n}\n\nconst isOnCall = (node: ts.Node): node is ts.CallExpression =>\n ts.isCallExpression(node) &&\n ts.isIdentifier(node.expression) &&\n node.expression.text === 'onCall';\n\nconst declaresTrue = (options: ts.ObjectLiteralExpression | null, key: string) =>\n !!options?.properties.some(\n (p) =>\n ts.isPropertyAssignment(p) &&\n p.name.getText().replace(/^['\"]|['\"]$/g, '') === key &&\n p.initializer.kind === ts.SyntaxKind.TrueKeyword,\n );\n\nexport function callablesOf(file: FileView): Callable[] {\n const sf = file.sourceFile;\n const optionsOf = (call: ts.CallExpression) =>\n call.arguments[0] && ts.isObjectLiteralExpression(call.arguments[0]) ? call.arguments[0] : null;\n const factories = new Map<string, ts.CallExpression>();\n const named = new Set<ts.CallExpression>();\n const out: Callable[] = [];\n forEachNode(sf, (node) => {\n if (ts.isFunctionDeclaration(node) && node.name && node.body) {\n forEachNode(node.body, (n) => {\n if (ts.isReturnStatement(n) && n.expression && isOnCall(n.expression))\n factories.set(node.name!.text, n.expression);\n });\n }\n });\n forEachNode(sf, (node) => {\n if (\n !ts.isVariableDeclaration(node) ||\n !ts.isIdentifier(node.name) ||\n !node.initializer ||\n !ts.isCallExpression(node.initializer)\n )\n return;\n const init: ts.CallExpression = node.initializer;\n if (isOnCall(init as ts.Node)) {\n named.add(init);\n out.push({ name: node.name.text, node: init, options: optionsOf(init) });\n } else if (ts.isIdentifier(init.expression) && factories.has(init.expression.text)) {\n const made = factories.get(init.expression.text)!;\n named.add(made);\n out.push({ name: node.name.text, node: init, options: optionsOf(made) });\n }\n });\n forEachNode(sf, (node) => {\n if (isOnCall(node) && !named.has(node))\n out.push({\n name: `(onCall at line ${lineOfNode(file, node)})`,\n node,\n options: optionsOf(node),\n });\n });\n return out;\n}\n\nexport function asksForAdmin(file: FileView, adminAuth: AppCheckFacts['adminAuth']): boolean {\n let found = false;\n forEachNode(file.sourceFile, (node) => {\n if (found || !ts.isCallExpression(node) || calleeName(node) !== adminAuth.call) return;\n found = node.arguments.some(\n (arg) =>\n ts.isObjectLiteralExpression(arg) &&\n arg.properties.some(\n (p) => p.name?.getText().replace(/^['\"]|['\"]$/g, '') === adminAuth.option,\n ),\n );\n });\n return found;\n}\n\nexport const appCheckGuard: GenericGuardDefinition<AppCheckFacts> = {\n id: 'appCheck',\n absentWhen: 'the app has no callable functions',\n factProblems(facts) {\n const problems: string[] = [];\n namesProblem(facts?.consumingFolders, 'consumingFolders', problems, { nonEmpty: false });\n textProblem(facts?.adminAuth?.call, 'adminAuth.call', problems);\n textProblem(facts?.adminAuth?.option, 'adminAuth.option', problems);\n reviewedProblems(facts?.consumptionExempt, 'consumptionExempt', problems, { key: 'callable' });\n declarationFactProblems(facts?.clientDeclarations, 'clientDeclarations', problems);\n reviewedProblems(facts?.noClientDeclaration, 'noClientDeclaration', problems, {\n key: 'callable',\n });\n return problems;\n },\n build(facts) {\n const posture: Guard = {\n title: 'every callable declares its App Check posture',\n description:\n 'Every `onCall` opens with a literal options object (one this check cannot read fails, never passes) that ' +\n 'declares `enforceAppCheck: true`: attestation sits beneath auth and neither substitutes for the other. A ' +\n `callable in a consuming folder (${facts.consumingFolders.join(', ') || 'none declared'}), and every callable in a ` +\n `file whose ${facts.adminAuth.call} asks for ${facts.adminAuth.option}, also declares \\`consumeAppCheckToken: true\\`, ` +\n 'so its attestation token is spent instead of reusable for its whole lifetime. A callable that keeps the cached ' +\n 'token is a reviewed exemption with its why; an exemption whose callable is gone or now consumes is stale.',\n run(context) {\n const findings: Finding[] = [];\n const exempt = new Map(facts.consumptionExempt.map((e) => [e.callable, e]));\n const exemptSeen = new Set<string>();\n const foldersWithCallables = new Set<string>();\n let callables = 0;\n for (const file of context.files(FUNCTIONS_CODE)) {\n const list = callablesOf(file);\n if (!list.length) continue;\n callables += list.length;\n const folder = facts.consumingFolders.find((f) => isUnder(file.path, f));\n if (folder) foldersWithCallables.add(folder);\n const mustConsume = !!folder || asksForAdmin(file, facts.adminAuth);\n for (const callable of list) {\n const line = lineOfNode(file, callable.node);\n if (!callable.options) {\n findings.push({\n file: file.path,\n line,\n detail: `${callable.name}: the onCall options are not an object literal, so the posture cannot be read`,\n });\n continue;\n }\n if (!declaresTrue(callable.options, 'enforceAppCheck'))\n findings.push({\n file: file.path,\n line,\n detail: `${callable.name}: no enforceAppCheck: true`,\n });\n const consumes = declaresTrue(callable.options, 'consumeAppCheckToken');\n if (exempt.has(callable.name)) {\n exemptSeen.add(callable.name);\n if (consumes)\n findings.push({\n file: file.path,\n line,\n detail: `stale exemption: ${callable.name} now consumes its token`,\n });\n else if (!mustConsume)\n findings.push({\n file: file.path,\n line,\n detail: `stale exemption: ${callable.name} is not a consuming callable`,\n });\n } else if (mustConsume && !consumes) {\n findings.push({\n file: file.path,\n line,\n detail: `${callable.name}: no consumeAppCheckToken: true`,\n });\n }\n }\n }\n if (!callables)\n findings.push({\n file: '(functions)',\n detail: 'no onCall found, so the guard checks nothing',\n });\n for (const folder of facts.consumingFolders)\n if (!foldersWithCallables.has(folder))\n findings.push({ file: folder, detail: 'stale consuming folder: it holds no callable' });\n for (const name of exempt.keys())\n if (!exemptSeen.has(name))\n findings.push({\n file: '(consumptionExempt)',\n detail: `stale exemption: no callable is named ${name}`,\n });\n return findings;\n },\n };\n const client: Guard = {\n title: \"a consuming callable's client declaration presents a limited-use token\",\n description:\n 'Every callable whose options declare `consumeAppCheckToken: true` has a client declaration in ' +\n `${facts.clientDeclarations.modules.join(', ')} carrying \\`limitedUseAppCheck: true\\`, so the token the server ` +\n 'spends is a single-use one rather than the cached token every other call reuses. A declaration carrying it names ' +\n 'a callable that consumes: one that names no callable, or a callable that keeps the cached token (an exempt one ' +\n 'included), mints a token per call for nothing. A consuming callable no client calls is a reviewed entry with ' +\n 'its why; an entry that is no longer a consuming callable, or that a declaration now names, is stale.',\n run(context) {\n const { declared, findings } = readDeclarations(context, facts.clientDeclarations);\n const servers = new Map<string, { consumes: boolean; file: string; line: number }>();\n for (const file of context.files(FUNCTIONS_CODE)) {\n for (const callable of callablesOf(file)) {\n servers.set(callable.name, {\n consumes: declaresTrue(callable.options, 'consumeAppCheckToken'),\n file: file.path,\n line: lineOfNode(file, callable.node),\n });\n }\n }\n if (!servers.size)\n findings.push({\n file: '(functions)',\n detail: 'no onCall found, so the guard checks nothing',\n });\n const byName = new Map(\n declared.filter((d) => d.callable !== null).map((d) => [d.callable!, d]),\n );\n const exempt = new Set(facts.consumptionExempt.map((e) => e.callable));\n const undeclared = new Set(facts.noClientDeclaration.map((e) => e.callable));\n for (const [name, server] of servers) {\n if (!server.consumes) continue;\n const declaration = byName.get(name);\n if (!declaration) {\n if (!undeclared.has(name))\n findings.push({\n file: server.file,\n line: server.line,\n detail: `${name} consumes its token, but no client declaration names it`,\n });\n } else if (!declaration.limitedUse) {\n findings.push({\n file: declaration.file,\n line: declaration.line,\n detail: `${declaration.hook} declares ${name}, which consumes its token, without limitedUseAppCheck: true`,\n });\n }\n }\n for (const declaration of declared) {\n if (!declaration.limitedUse || declaration.callable === null) continue;\n const server = servers.get(declaration.callable);\n const where = { file: declaration.file, line: declaration.line };\n if (!server)\n findings.push({\n ...where,\n detail: `${declaration.hook} declares ${declaration.callable} limited-use, but no callable of that name exists`,\n });\n else if (exempt.has(declaration.callable))\n findings.push({\n ...where,\n detail: `${declaration.hook} declares ${declaration.callable} limited-use, but it is exempt from consumption`,\n });\n else if (!server.consumes)\n findings.push({\n ...where,\n detail: `${declaration.hook} declares ${declaration.callable} limited-use, but it does not consume its token`,\n });\n }\n for (const name of undeclared) {\n if (byName.has(name))\n findings.push({\n file: '(noClientDeclaration)',\n detail: `stale entry: a client declaration names ${name} now`,\n });\n else if (!servers.get(name)?.consumes)\n findings.push({\n file: '(noClientDeclaration)',\n detail: `stale entry: ${name} is not a consuming callable`,\n });\n }\n return findings;\n },\n };\n return [posture, client];\n },\n};\n"]}
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/modules/backend/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAM5B,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAA0B,MAAM,wBAAwB,CAAC;AAC9F,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EACL,MAAM,EACN,YAAY,EACZ,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,WAAW,GACZ,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EACL,uBAAuB,EACvB,gBAAgB,GAEjB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,GAEnB,MAAM,aAAa,CAAC;AAErB,MAAM,cAAc,GAAU,EAAE,KAAK,EAAE,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAQtF,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAA,kDAAkD,CAAC;AAEpF,MAAM,UAAU,iBAAiB,CAAC,SAA4B;IAC5D,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG;QACf,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAA,KAAK,cAAc,uBAAuB,IAAI,KAAK,EAAE,GAAG,CAAC;QAC9E,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAA,KAAK,cAAc,kCAAkC,IAAI,UAAU,EAAE,GAAG,CAAC;QAC9F,IAAI,MAAM,CACR,MAAM,CAAC,GAAG,CAAA,kBAAkB,IAAI,8BAA8B,cAAc,IAAI,EAChF,GAAG,CACJ;KACF,CAAC;IACF,OAAO,CAAC,IAAI,EAAE,EAAE;QACd,MAAM,IAAI,GAAU,EAAE,CAAC;QACvB,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC1B,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;YAC1C,KAAK,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;gBACtD,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;QACpE,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IAChD,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,eAAe,GAA4C;IACtE,EAAE,EAAE,gBAAgB;IACpB,UAAU,EAAE,4BAA4B;IACxC,YAAY,CAAC,KAAK;QAChB,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,YAAY,CAAC,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;QACtD,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1E,kBAAkB,CAAC,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,QAAQ,CAAC,CAAC;QACpE,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,KAAK,CAAC,KAAK;QACT,MAAM,KAAK,GAAG,iBAAiB,CAAC;YAC9B,KAAK,EAAE,8CAA8C;YACrD,WAAW,EACT,0CAA0C,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,8CAA8C;gBAClH,gHAAgH;gBAChH,+GAA+G;gBAC/G,kHAAkH;gBAClH,iFAAiF;YACnF,KAAK,EAAE;gBACL,KAAK,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC;gBAC3B,aAAa,EAAE,CAAC,WAAW,CAAC;gBAC5B,KAAK,EAAE,CAAC,MAAM,CAAC;gBACf,KAAK,EAAE,KAAK;aACb;YACD,KAAK,EAAE,iBAAiB,CAAC,KAAK,CAAC,SAAS,CAAC;YACzC,SAAS,EAAE,KAAK,CAAC,OAAO;YACxB,OAAO,EAAE;gBACP,KAAK,EAAE;oBACL,0BAA0B,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,wBAAwB;oBACpE,wBAAwB,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK;oBAC/C,gBAAgB,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,yBAAyB;iBAC5D;gBACD,MAAM,EAAE;oBACN,yCAAyC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,wBAAwB,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU;iBAChH;aACF;SACF,CAAC,CAAC;QACH,OAAO;YACL;gBACE,GAAG,KAAK;gBACR,KAAK,CAAC,GAAG,CAAC,OAAqB;oBAC7B,OAAO;wBACL,GAAG,kBAAkB,CAAC,OAAO,EAAE,KAAK,CAAC,aAAa,CAAC;wBACnD,GAAG,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,OAAO,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC;qBACtE,CAAC;gBACJ,CAAC;aACF;SACF,CAAC;IACJ,CAAC;CACF,CAAC;AAMF,MAAM,WAAW,GAAG,oDAAoD,CAAC;AAEzE,MAAM,CAAC,MAAM,kBAAkB,GAA+C;IAC5E,EAAE,EAAE,eAAe;IACnB,UAAU,EAAE,KAAK;IACjB,YAAY,CAAC,KAAK;QAChB,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC/C,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,KAAK,CAAC,KAAK;QACT,OAAO;YACL,SAAS,CAAC;gBACR,KAAK,EAAE,yCAAyC;gBAChD,WAAW,EAAE,sCAAsC,KAAK,CAAC,MAAM,mCAAmC;gBAClG,KAAK,EAAE,cAAc;gBACrB,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE;oBACd,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC;oBAC7D,MAAM,IAAI,GAAU,EAAE,CAAC;oBACvB,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;wBACxD,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;oBAC9C,OAAO,IAAI,CAAC;gBACd,CAAC;gBACD,MAAM,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC;gBACtB,OAAO,EAAE;oBACP,KAAK,EAAE;wBACL,4CAA4C;wBAC5C,0CAA0C;wBAC1C,wBAAwB;wBACxB,iCAAiC;qBAClC;oBACD,MAAM,EAAE,CAAC,6EAA6E,CAAC;iBACxF;aACF,CAAC;SACH,CAAC;IACJ,CAAC;CACF,CAAC;AAIF,MAAM,eAAe,GAAG,yCAAyC,CAAC;AAClE,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,CAAC,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC,CAAC;AAEhG,MAAM,UAAU,qBAAqB,CAAC,IAAc;IAClD,MAAM,GAAG,GAA6C,EAAE,CAAC;IACzD,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC;QACvE,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,IAAI,EAAE,EAAE,CAAC,CAAC;IAChF,WAAW,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE;QACpC,IACE,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC;YACzB,EAAE,CAAC,0BAA0B,CAAC,IAAI,CAAC,UAAU,CAAC;YAC9C,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAChD,CAAC;YACD,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACvE,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,MAAM,uBAAuB,GAAoD;IACtF,EAAE,EAAE,wBAAwB;IAC5B,UAAU,EAAE,KAAK;IACjB,YAAY,EAAE,YAAY;IAC1B,KAAK;QACH,OAAO;YACL;gBACE,KAAK,EAAE,yCAAyC;gBAChD,WAAW,EACT,qGAAqG;oBACrG,yGAAyG;oBACzG,4GAA4G;oBAC5G,sGAAsG;gBACxG,GAAG,CAAC,OAAO;oBACT,OAAO,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CACpD,qBAAqB,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;wBACrD,IAAI,EAAE,IAAI,CAAC,IAAI;wBACf,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC;wBAC5B,MAAM;qBACP,CAAC,CAAC,CACJ,CAAC;gBACJ,CAAC;aACF;SACF,CAAC;IACJ,CAAC;CACF,CAAC;AAgBF,MAAM,QAAQ,GAAG,CAAC,IAAa,EAA6B,EAAE,CAC5D,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC;IACzB,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC;IAChC,IAAI,CAAC,UAAU,CAAC,IAAI,KAAK,QAAQ,CAAC;AAEpC,MAAM,YAAY,GAAG,CAAC,OAA0C,EAAE,GAAW,EAAE,EAAE,CAC/E,CAAC,CAAC,OAAO,EAAE,UAAU,CAAC,IAAI,CACxB,CAAC,CAAC,EAAE,EAAE,CACJ,EAAE,CAAC,oBAAoB,CAAC,CAAC,CAAC;IAC1B,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,KAAK,GAAG;IACpD,CAAC,CAAC,WAAW,CAAC,IAAI,KAAK,EAAE,CAAC,UAAU,CAAC,WAAW,CACnD,CAAC;AAEJ,MAAM,UAAU,WAAW,CAAC,IAAc;IACxC,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC;IAC3B,MAAM,SAAS,GAAG,CAAC,IAAuB,EAAE,EAAE,CAC5C,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,yBAAyB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAClG,MAAM,SAAS,GAAG,IAAI,GAAG,EAA6B,CAAC;IACvD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAqB,CAAC;IAC3C,MAAM,GAAG,GAAe,EAAE,CAAC;IAC3B,WAAW,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE;QACvB,IAAI,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAC7D,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE;gBAC3B,IAAI,EAAE,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,IAAI,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC;oBACnE,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAK,CAAC,IAAI,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC;YACjD,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IACH,WAAW,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE;QACvB,IACE,CAAC,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC;YAC/B,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;YAC3B,CAAC,IAAI,CAAC,WAAW;YACjB,CAAC,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,WAAW,CAAC;YAEtC,OAAO;QACT,MAAM,IAAI,GAAsB,IAAI,CAAC,WAAW,CAAC;QACjD,IAAI,QAAQ,CAAC,IAAe,CAAC,EAAE,CAAC;YAC9B,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAChB,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC3E,CAAC;aAAM,IAAI,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACnF,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAE,CAAC;YAClD,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAChB,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC,CAAC,CAAC;IACH,WAAW,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE;QACvB,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;YACpC,GAAG,CAAC,IAAI,CAAC;gBACP,IAAI,EAAE,mBAAmB,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG;gBAClD,IAAI;gBACJ,OAAO,EAAE,SAAS,CAAC,IAAI,CAAC;aACzB,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IACH,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAc,EAAE,SAAqC;IAChF,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,WAAW,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE;QACpC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC,IAAI;YAAE,OAAO;QACvF,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CACzB,CAAC,GAAG,EAAE,EAAE,CACN,EAAE,CAAC,yBAAyB,CAAC,GAAG,CAAC;YACjC,GAAG,CAAC,UAAU,CAAC,IAAI,CACjB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,KAAK,SAAS,CAAC,MAAM,CAC1E,CACJ,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAA0C;IAClE,EAAE,EAAE,UAAU;IACd,UAAU,EAAE,mCAAmC;IAC/C,YAAY,CAAC,KAAK;QAChB,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,YAAY,CAAC,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;QACzF,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,gBAAgB,EAAE,QAAQ,CAAC,CAAC;QAChE,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,kBAAkB,EAAE,QAAQ,CAAC,CAAC;QACpE,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,QAAQ,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC,CAAC;QAC/F,uBAAuB,CAAC,KAAK,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,QAAQ,CAAC,CAAC;QACnF,gBAAgB,CAAC,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,QAAQ,EAAE;YAC5E,GAAG,EAAE,UAAU;SAChB,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,KAAK,CAAC,KAAK;QACT,MAAM,OAAO,GAAU;YACrB,KAAK,EAAE,+CAA+C;YACtD,WAAW,EACT,2GAA2G;gBAC3G,2GAA2G;gBAC3G,mCAAmC,KAAK,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,eAAe,6BAA6B;gBACpH,cAAc,KAAK,CAAC,SAAS,CAAC,IAAI,aAAa,KAAK,CAAC,SAAS,CAAC,MAAM,kDAAkD;gBACvH,iHAAiH;gBACjH,2GAA2G;YAC7G,GAAG,CAAC,OAAO;gBACT,MAAM,QAAQ,GAAc,EAAE,CAAC;gBAC/B,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC5E,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;gBACrC,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAU,CAAC;gBAC/C,IAAI,SAAS,GAAG,CAAC,CAAC;gBAClB,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC;oBACjD,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;oBAC/B,IAAI,CAAC,IAAI,CAAC,MAAM;wBAAE,SAAS;oBAC3B,SAAS,IAAI,IAAI,CAAC,MAAM,CAAC;oBACzB,MAAM,MAAM,GAAG,KAAK,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;oBACzE,IAAI,MAAM;wBAAE,oBAAoB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;oBAC7C,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,IAAI,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;oBACpE,KAAK,MAAM,QAAQ,IAAI,IAAI,EAAE,CAAC;wBAC5B,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;wBAC7C,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;4BACtB,QAAQ,CAAC,IAAI,CAAC;gCACZ,IAAI,EAAE,IAAI,CAAC,IAAI;gCACf,IAAI;gCACJ,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,+EAA+E;6BACxG,CAAC,CAAC;4BACH,SAAS;wBACX,CAAC;wBACD,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;4BACpD,QAAQ,CAAC,IAAI,CAAC;gCACZ,IAAI,EAAE,IAAI,CAAC,IAAI;gCACf,IAAI;gCACJ,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,4BAA4B;6BACrD,CAAC,CAAC;wBACL,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC;wBACxE,IAAI,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;4BAC9B,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;4BAC9B,IAAI,QAAQ;gCACV,QAAQ,CAAC,IAAI,CAAC;oCACZ,IAAI,EAAE,IAAI,CAAC,IAAI;oCACf,IAAI;oCACJ,MAAM,EAAE,oBAAoB,QAAQ,CAAC,IAAI,yBAAyB;iCACnE,CAAC,CAAC;iCACA,IAAI,CAAC,WAAW;gCACnB,QAAQ,CAAC,IAAI,CAAC;oCACZ,IAAI,EAAE,IAAI,CAAC,IAAI;oCACf,IAAI;oCACJ,MAAM,EAAE,oBAAoB,QAAQ,CAAC,IAAI,8BAA8B;iCACxE,CAAC,CAAC;wBACP,CAAC;6BAAM,IAAI,WAAW,IAAI,CAAC,QAAQ,EAAE,CAAC;4BACpC,QAAQ,CAAC,IAAI,CAAC;gCACZ,IAAI,EAAE,IAAI,CAAC,IAAI;gCACf,IAAI;gCACJ,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,iCAAiC;6BAC1D,CAAC,CAAC;wBACL,CAAC;oBACH,CAAC;gBACH,CAAC;gBACD,IAAI,CAAC,SAAS;oBACZ,QAAQ,CAAC,IAAI,CAAC;wBACZ,IAAI,EAAE,aAAa;wBACnB,MAAM,EAAE,8CAA8C;qBACvD,CAAC,CAAC;gBACL,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,gBAAgB;oBACzC,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,MAAM,CAAC;wBACnC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,8CAA8C,EAAE,CAAC,CAAC;gBAC5F,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE;oBAC9B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC;wBACvB,QAAQ,CAAC,IAAI,CAAC;4BACZ,IAAI,EAAE,qBAAqB;4BAC3B,MAAM,EAAE,yCAAyC,IAAI,EAAE;yBACxD,CAAC,CAAC;gBACP,OAAO,QAAQ,CAAC;YAClB,CAAC;SACF,CAAC;QACF,MAAM,MAAM,GAAU;YACpB,KAAK,EAAE,wEAAwE;YAC/E,WAAW,EACT,gGAAgG;gBAChG,GAAG,KAAK,CAAC,kBAAkB,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,kEAAkE;gBAChH,mHAAmH;gBACnH,iHAAiH;gBACjH,+GAA+G;gBAC/G,sGAAsG;YACxG,GAAG,CAAC,OAAO;gBACT,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,kBAAkB,CAAC,CAAC;gBACnF,MAAM,OAAO,GAAG,IAAI,GAAG,EAA6D,CAAC;gBACrF,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC;oBACjD,KAAK,MAAM,QAAQ,IAAI,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;wBACzC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE;4BACzB,QAAQ,EAAE,YAAY,CAAC,QAAQ,CAAC,OAAO,EAAE,sBAAsB,CAAC;4BAChE,IAAI,EAAE,IAAI,CAAC,IAAI;4BACf,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC;yBACtC,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;gBACD,IAAI,CAAC,OAAO,CAAC,IAAI;oBACf,QAAQ,CAAC,IAAI,CAAC;wBACZ,IAAI,EAAE,aAAa;wBACnB,MAAM,EAAE,8CAA8C;qBACvD,CAAC,CAAC;gBACL,MAAM,MAAM,GAAG,IAAI,GAAG,CACpB,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAS,EAAE,CAAC,CAAC,CAAC,CACzE,CAAC;gBACF,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;gBACvE,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;gBAC7E,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;oBACrC,IAAI,CAAC,MAAM,CAAC,QAAQ;wBAAE,SAAS;oBAC/B,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;oBACrC,IAAI,CAAC,WAAW,EAAE,CAAC;wBACjB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC;4BACvB,QAAQ,CAAC,IAAI,CAAC;gCACZ,IAAI,EAAE,MAAM,CAAC,IAAI;gCACjB,IAAI,EAAE,MAAM,CAAC,IAAI;gCACjB,MAAM,EAAE,GAAG,IAAI,yDAAyD;6BACzE,CAAC,CAAC;oBACP,CAAC;yBAAM,IAAI,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC;wBACnC,QAAQ,CAAC,IAAI,CAAC;4BACZ,IAAI,EAAE,WAAW,CAAC,IAAI;4BACtB,IAAI,EAAE,WAAW,CAAC,IAAI;4BACtB,MAAM,EAAE,GAAG,WAAW,CAAC,IAAI,aAAa,IAAI,8DAA8D;yBAC3G,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;gBACD,KAAK,MAAM,WAAW,IAAI,QAAQ,EAAE,CAAC;oBACnC,IAAI,CAAC,WAAW,CAAC,UAAU,IAAI,WAAW,CAAC,QAAQ,KAAK,IAAI;wBAAE,SAAS;oBACvE,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;oBACjD,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC;oBACjE,IAAI,CAAC,MAAM;wBACT,QAAQ,CAAC,IAAI,CAAC;4BACZ,GAAG,KAAK;4BACR,MAAM,EAAE,GAAG,WAAW,CAAC,IAAI,aAAa,WAAW,CAAC,QAAQ,mDAAmD;yBAChH,CAAC,CAAC;yBACA,IAAI,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC;wBACvC,QAAQ,CAAC,IAAI,CAAC;4BACZ,GAAG,KAAK;4BACR,MAAM,EAAE,GAAG,WAAW,CAAC,IAAI,aAAa,WAAW,CAAC,QAAQ,iDAAiD;yBAC9G,CAAC,CAAC;yBACA,IAAI,CAAC,MAAM,CAAC,QAAQ;wBACvB,QAAQ,CAAC,IAAI,CAAC;4BACZ,GAAG,KAAK;4BACR,MAAM,EAAE,GAAG,WAAW,CAAC,IAAI,aAAa,WAAW,CAAC,QAAQ,iDAAiD;yBAC9G,CAAC,CAAC;gBACP,CAAC;gBACD,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;oBAC9B,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;wBAClB,QAAQ,CAAC,IAAI,CAAC;4BACZ,IAAI,EAAE,uBAAuB;4BAC7B,MAAM,EAAE,2CAA2C,IAAI,MAAM;yBAC9D,CAAC,CAAC;yBACA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ;wBACnC,QAAQ,CAAC,IAAI,CAAC;4BACZ,IAAI,EAAE,uBAAuB;4BAC7B,MAAM,EAAE,gBAAgB,IAAI,8BAA8B;yBAC3D,CAAC,CAAC;gBACP,CAAC;gBACD,OAAO,QAAQ,CAAC;YAClB,CAAC;SACF,CAAC;QACF,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC3B,CAAC;CACF,CAAC;AAgBF,MAAM,eAAe,GAAU,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAEjF,SAAS,WAAW,CAAC,IAAa,EAAE,SAA+C;IACjF,MAAM,KAAK,GAA8B,EAAE,CAAC;IAC5C,WAAW,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE;QACzB,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC,IAAI;YAAE,OAAO;QAC9E,KAAK,CAAC,IAAI,CAAC;YACT,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CACxB,CAAC,GAAG,EAAE,EAAE,CACN,EAAE,CAAC,yBAAyB,CAAC,GAAG,CAAC;gBACjC,GAAG,CAAC,UAAU,CAAC,IAAI,CACjB,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,KAAK,SAAS,CAAC,MAAM;oBAClE,CAAC,CAAC,EAAE,CAAC,oBAAoB,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAC/D,CACJ;SACF,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,YAAY,GAAG,CAAC,KAAoB,EAAW,EAAE,CACrD,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC,UAAU,CAAC,YAAY;IACzC,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC,UAAU,CAAC,WAAW;IACxC,CAAC,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC;AAEzD,MAAM,UAAU,kBAAkB,CAChC,IAAc,EACd,SAA+C;IAE/C,MAAM,WAAW,GAAG,WAAW,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IAC5D,OAAO,WAAW,CAAC,IAAI,CAAC;SACrB,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE;QACnB,MAAM,GAAG,GAAG,WAAW,CAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QAClD,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC;QAC7C,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACzD,CAAC,CAAC;SACD,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACtC,CAAC;AAQD,MAAM,UAAU,eAAe,CAC7B,IAAc,EACd,KAAgE,EAChE,mBAAoD;IAEpD,MAAM,GAAG,GAAqB,EAAE,CAAC;IACjC,MAAM,IAAI,GAAG,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAClF,KAAK,MAAM,GAAG,IAAI,iBAAiB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC;QACxD,GAAG,CAAC,IAAI,CAAC;YACP,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE;YAC9C,MAAM,EAAE,0BAA0B,GAAG,CAAC,MAAM,GAAG;SAChD,CAAC,CAAC;IACL,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACxC,KAAK,MAAM,IAAI,IAAI,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE,CAAC;QAChE,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAClD,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9C,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;YAChD,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;gBAC/C,GAAG,CAAC,IAAI,CAAC;oBACP,KAAK;oBACL,MAAM;oBACN,MAAM,EAAE,WAAW,MAAM,gCAAgC,MAAM,EAAE;iBAClE,CAAC,CAAC;YACL,SAAS;QACX,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK;YAC3B,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAC1B,GAAG,CAAC,IAAI,CAAC;oBACP,KAAK;oBACL,MAAM,EAAE,IAAI,CAAC,QAAQ;oBACrB,MAAM,EAAE,0BAA0B,IAAI,CAAC,QAAQ,EAAE;iBAClD,CAAC,CAAC;IACT,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;AAC/C,CAAC;AAED,SAAS,iBAAiB,CAAC,KAA0C,EAAE,QAAkB;IACvF,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM;QACvE,QAAQ,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;;QACxC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,kBAAkB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC9F,WAAW,CAAC,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,QAAQ,CAAC,CAAC;IACjE,gBAAgB,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE;QACtD,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CACR,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS;QAC/F,KAAK,CAAC,KAAK,EAAE,KAAK;YAChB,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;gBACrD,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,oBAAoB,CAAC,CAAC;QAChD,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,uBAAuB,GAAoD;IACtF,EAAE,EAAE,oBAAoB;IACxB,UAAU,EAAE,8BAA8B;IAC1C,YAAY,CAAC,KAAK;QAChB,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,IAAI,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,CAAC;YAC3D,WAAW,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,gBAAgB,EAAE,QAAQ,CAAC,CAAC;YAC9D,WAAW,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,EAAE,kBAAkB,EAAE,QAAQ,CAAC,CAAC;QACpE,CAAC;QACD,YAAY,CAAC,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,QAAQ,CAAC,CAAC;QAC9D,gBAAgB,CAAC,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,QAAQ,CAAC,CAAC;QACpE,uBAAuB,CAAC,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,QAAQ,CAAC,CAAC;QACjF,YAAY,CAAC,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;QACtD,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7E,iBAAiB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACnC,aAAa,CAAC,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,QAAQ,CAAC,CAAC;QAC/D,kBAAkB,CAAC,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,QAAQ,CAAC,CAAC;QACpE,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,KAAK,CAAC,KAAK;QACT,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QACpE,MAAM,SAAS,GAAG,GAAG,KAAK,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,eAAe,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,WAAW,CAAC;QAC3G,OAAO;YACL;gBACE,KAAK,EAAE,8EAA8E;gBACrF,WAAW,EACT,qDAAqD,KAAK,CAAC,SAAS,CAAC,IAAI,mBAAmB;oBAC5F,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,sFAAsF;oBAC/G,2CAA2C,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,kCAAkC;oBAC3G,YAAY,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,0DAA0D;oBAChH,qCAAqC,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,qCAAqC;oBACzG,IAAI,KAAK,CAAC,eAAe,uFAAuF;oBAChH,2GAA2G;oBAC3G,sGAAsG;gBACxG,OAAO,EAAE;oBACP,KAAK,EAAE;wBACL,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,eAAe,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,EAAE;wBACvE,GAAG,KAAK,CAAC,UAAU;6BAChB,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;6BACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,IAAI,oBAAoB,EAAE,CAAC,CAAC;qBACpF;oBACD,MAAM,EAAE;wBACN,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,kBAAkB,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE;wBACnE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,oCAAoC,EAAE;qBAChE;oBACD,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC;iBAC1D;gBACD,GAAG,CAAC,OAAO;oBACT,MAAM,QAAQ,GAAc,EAAE,CAAC;oBAC/B,MAAM,cAAc,GAAG,IAAI,GAAG,EAA0C,CAAC;oBACzE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;oBAC1E,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;oBAC9C,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC;wBACjD,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;wBACpC,IAAI,CAAC,SAAS,CAAC,MAAM;4BAAE,SAAS;wBAChC,KAAK,MAAM,IAAI,IAAI,kBAAkB,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;4BAC7D,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAE,CAAC;4BACzD,cAAc,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;wBACvF,CAAC;wBACD,WAAW,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE;4BACpC,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC;gCAAE,OAAO;4BACvC,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;4BAC9B,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;gCAAE,OAAO;4BACzC,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;gCAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;;gCAEvD,QAAQ,CAAC,IAAI,CAAC;oCACZ,IAAI,EAAE,IAAI,CAAC,IAAI;oCACf,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC;oCAC5B,MAAM,EAAE,mDAAmD,IAAI,mBAAmB,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE;iCACzG,CAAC,CAAC;wBACP,CAAC,CAAC,CAAC;oBACL,CAAC;oBACD,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,OAAO;wBAChC,IAAI,CAAC,IAAI;4BACP,QAAQ,CAAC,IAAI,CAAC;gCACZ,IAAI,EAAE,IAAI;gCACV,MAAM,EAAE,wEAAwE;6BACjF,CAAC,CAAC;oBACP,IAAI,CAAC,cAAc,CAAC,IAAI;wBACtB,QAAQ,CAAC,IAAI,CAAC;4BACZ,IAAI,EAAE,aAAa;4BACnB,MAAM,EAAE,oBAAoB,KAAK,CAAC,SAAS,CAAC,IAAI,QAAQ,KAAK,CAAC,SAAS,CAAC,MAAM,uCAAuC;yBACtH,CAAC,CAAC;oBAEL,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,mBAAmB,EAAE,GAAG,gBAAgB,CAClE,OAAO,EACP,KAAK,CAAC,iBAAiB,CACxB,CAAC;oBACF,QAAQ,CAAC,IAAI,CAAC,GAAG,mBAAmB,CAAC,CAAC;oBACtC,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;oBACxC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;wBACzB,IAAI,CAAC,CAAC,QAAQ,KAAK,IAAI;4BAAE,SAAS;wBAClC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;wBAC9B,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC;4BACjC,QAAQ,CAAC,IAAI,CAAC;gCACZ,IAAI,EAAE,CAAC,CAAC,IAAI;gCACZ,IAAI,EAAE,CAAC,CAAC,IAAI;gCACZ,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,QAAQ,uCAAuC;6BAChF,CAAC,CAAC;oBACP,CAAC;oBACD,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,cAAc;wBACxC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;4BAC1B,QAAQ,CAAC,IAAI,CAAC;gCACZ,GAAG,KAAK;gCACR,MAAM,EAAE,GAAG,IAAI,uBAAuB,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,sBAAsB;6BACvG,CAAC,CAAC;oBAEP,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,cAAc;wBACrC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;4BAC9B,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,4CAA4C,EAAE,CAAC,CAAC;oBACxF,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;oBACjE,IAAI,CAAC,WAAW;wBACd,QAAQ,CAAC,IAAI,CAAC;4BACZ,IAAI,EAAE,KAAK,CAAC,eAAe;4BAC3B,MAAM,EAAE,+CAA+C;yBACxD,CAAC,CAAC;oBACL,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;oBAC/C,MAAM,WAAW,GAAG,CAAC,CAAoC,EAAE,EAAE,CAC3D,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;oBAC/C,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;oBACvC,IAAI,UAAU,GAAG,KAAK,CAAC;oBACvB,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,EAAE,CAAC;wBAClD,IAAI,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,aAAa,CAAC;4BAAE,SAAS;wBACtD,IAAI,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;4BAAE,SAAS;wBAC5E,MAAM,IAAI,GAAG,eAAe,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE;4BACjD,MAAM,QAAQ,GAAG,gBAAgB,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;4BAC5E,OAAO,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;wBAC5D,CAAC,CAAC,CAAC;wBACH,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,eAAe,EAAE,CAAC;4BACxC,UAAU,KAAK,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;4BAC/B,SAAS;wBACX,CAAC;wBACD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;4BACvB,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAC/B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,MAAM,CAAC,CACnF,CAAC;4BACF,IAAI,KAAK;gCAAE,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;;gCAE9C,QAAQ,CAAC,IAAI,CAAC;oCACZ,IAAI,EAAE,IAAI,CAAC,IAAI;oCACf,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;oCAC5B,MAAM,EAAE,2BAA2B,GAAG,CAAC,MAAM,EAAE;iCAChD,CAAC,CAAC;wBACP,CAAC;oBACH,CAAC;oBACD,IAAI,WAAW,IAAI,CAAC,UAAU;wBAC5B,QAAQ,CAAC,IAAI,CAAC;4BACZ,IAAI,EAAE,KAAK,CAAC,eAAe;4BAC3B,MAAM,EACJ,6FAA6F;yBAChG,CAAC,CAAC;oBACL,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,QAAQ;wBAChC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;4BACvC,QAAQ,CAAC,IAAI,CAAC;gCACZ,IAAI,EAAE,KAAK,CAAC,IAAI;gCAChB,MAAM,EAAE,iDAAiD,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;6BAC1G,CAAC,CAAC;oBACP,OAAO,QAAQ,CAAC;gBAClB,CAAC;aACF;SACF,CAAC;IACJ,CAAC;CACF,CAAC","sourcesContent":["import ts from 'typescript';\nimport type { GenericGuardDefinition } from '../../node/suite/config.js';\nimport type { Finding, Guard, GuardContext } from '../../guard.js';\nimport type { Scope } from '../../node/reader.js';\nimport type { Reviewed } from '../../node/layout.js';\nimport type { FileView } from '../../reader/view.js';\nimport { ownerOnly, reviewedAllowlist, type Hit, type Matcher } from '../../shapes/shapes.js';\nimport { calleeName, forEachNode, lineOfNode } from '../../support/ast.js';\nimport {\n isText,\n namesProblem,\n objectProblem,\n pathProblem,\n reviewedProblems,\n takesNoFacts,\n textProblem,\n} from '../../support/facts.js';\nimport { MODULE_LOADS, moduleImports } from '../../support/imports.js';\nimport { aliasList, resolveSpecifier } from '../../support/resolve-specifier.js';\nimport { escapeRegExp, isUnder } from '../../support/text.js';\nimport {\n declarationFactProblems,\n readDeclarations,\n type CallableDeclarationFacts,\n} from '../frontend-actions/declarations.js';\nimport {\n inServerZone,\n serverZoneContext,\n serverZoneProblems,\n staleServerFolders,\n type ServerZone,\n} from './shared.js';\n\nconst FUNCTIONS_CODE: Scope = { roots: ['functions'], kinds: ['code'], tests: false };\n\nexport interface AdminClaimFacts {\n claimKeys: readonly string[];\n readers: readonly Reviewed[];\n appServerZone: ServerZone;\n}\n\nconst CLAIM_RECEIVER = String.raw`(?:[\\w$]*[Tt]oken|[\\w$]*[Cc]laims|decoded[\\w$]*)`;\n\nexport function adminClaimMatcher(claimKeys: readonly string[]): Matcher {\n const keys = claimKeys.map(escapeRegExp).join('|');\n const patterns = [\n new RegExp(String.raw`\\b${CLAIM_RECEIVER}\\s*(?:\\?\\.|\\.)\\s*(?:${keys})\\b`, 'g'),\n new RegExp(String.raw`\\b${CLAIM_RECEIVER}\\s*(?:\\?\\.)?\\s*\\[\\s*(['\"\\`])(?:${keys})\\1\\s*\\]`, 'g'),\n new RegExp(\n String.raw`\\{[^{}\\n]*\\b(?:${keys})\\b[^{}\\n]*\\}\\s*=\\s*[^;\\n]*${CLAIM_RECEIVER}\\b`,\n 'g',\n ),\n ];\n return (file) => {\n const hits: Hit[] = [];\n for (const re of patterns) {\n const g = new RegExp(re.source, re.flags);\n for (let m = g.exec(file.code); m; m = g.exec(file.code))\n hits.push({ index: m.index, detail: m[0].trim().slice(0, 80) });\n }\n return hits.sort((a, b) => a.index - b.index);\n };\n}\n\nexport const adminClaimGuard: GenericGuardDefinition<AdminClaimFacts> = {\n id: 'adminClaimRead',\n absentWhen: 'the app has no admin claim',\n factProblems(facts) {\n const problems: string[] = [];\n namesProblem(facts?.claimKeys, 'claimKeys', problems);\n reviewedProblems(facts?.readers, 'readers', problems, { nonEmpty: true });\n serverZoneProblems(facts?.appServerZone, 'appServerZone', problems);\n return problems;\n },\n build(facts) {\n const guard = reviewedAllowlist({\n title: 'the admin claim is read only by its resolver',\n description:\n `No server module reads an admin claim (${facts.claimKeys.join(', ')}) off a token or claims payload — by member ` +\n 'access, bracket access, or destructuring — outside the reviewed readers. The claim is metadata and stays true ' +\n 'for up to an hour after a demotion, and anything signed from it outlives the revocation by its own lifetime. ' +\n \"Resolve admin standing through the resolver's authoritative read, failing closed on a read failure. Writing the \" +\n 'claim is not a read. A reviewed reader that no longer reads the claim is stale.',\n scope: {\n roots: ['functions', 'app'],\n optionalRoots: ['functions'],\n kinds: ['code'],\n tests: false,\n },\n match: adminClaimMatcher(facts.claimKeys),\n allowlist: facts.readers,\n samples: {\n flags: [\n `if (request.auth.token.${facts.claimKeys[0]} === true) return all;`,\n `const a = decoded?.['${facts.claimKeys[0]}'];`,\n `const { uid, ${facts.claimKeys[0]} = false } = ctx.token;`,\n ],\n passes: [\n `await auth.setCustomUserClaims(uid, { ${facts.claimKeys[0]}: true });\\nif (args.${facts.claimKeys[0]}) run();`,\n ],\n },\n });\n return [\n {\n ...guard,\n async run(context: GuardContext) {\n return [\n ...staleServerFolders(context, facts.appServerZone),\n ...(await guard.run(serverZoneContext(context, facts.appServerZone))),\n ];\n },\n },\n ];\n },\n};\n\nexport interface RequestOriginFacts {\n reader: string;\n}\n\nconst ORIGIN_READ = /\\brawRequest\\b|x-forwarded-for|\\breq\\s*\\.\\s*ip\\b/gi;\n\nexport const requestOriginGuard: GenericGuardDefinition<RequestOriginFacts> = {\n id: 'requestOrigin',\n absentWhen: false,\n factProblems(facts) {\n const problems: string[] = [];\n textProblem(facts?.reader, 'reader', problems);\n return problems;\n },\n build(facts) {\n return [\n ownerOnly({\n title: \"a request's origin is read in one place\",\n description: `A request's origin is read only in ${facts.reader}, which must still hold the read.`,\n scope: FUNCTIONS_CODE,\n match: (file) => {\n const re = new RegExp(ORIGIN_READ.source, ORIGIN_READ.flags);\n const hits: Hit[] = [];\n for (let m = re.exec(file.code); m; m = re.exec(file.code))\n hits.push({ index: m.index, detail: m[0] });\n return hits;\n },\n owners: [facts.reader],\n samples: {\n flags: [\n 'const ip = request.rawRequest?.ip ?? null;',\n \"const hops = headers['x-forwarded-for'];\",\n 'const caller = req.ip;',\n 'const { rawRequest } = request;',\n ],\n passes: ['const facts = { ip: context.ip };\\n// rawRequest is read only by the reader'],\n },\n }),\n ];\n },\n};\n\nexport type NoBlockingFunctionFacts = Record<string, never>;\n\nconst BLOCKING_MODULE = /^firebase-functions\\/(?:v2\\/)?identity$/;\nconst BLOCKING_BUILDERS = new Set(['beforeCreate', 'beforeSignIn', 'beforeEmail', 'beforeSms']);\n\nexport function blockingFunctionSites(file: FileView): Array<{ node: ts.Node; detail: string }> {\n const out: Array<{ node: ts.Node; detail: string }> = [];\n for (const { spec, node } of moduleImports(file.sourceFile, MODULE_LOADS))\n if (BLOCKING_MODULE.test(spec)) out.push({ node, detail: `imports ${spec}` });\n forEachNode(file.sourceFile, (node) => {\n if (\n ts.isCallExpression(node) &&\n ts.isPropertyAccessExpression(node.expression) &&\n BLOCKING_BUILDERS.has(node.expression.name.text)\n ) {\n out.push({ node, detail: `registers ${node.expression.name.text}` });\n }\n });\n return out;\n}\n\nexport const noBlockingFunctionGuard: GenericGuardDefinition<NoBlockingFunctionFacts> = {\n id: 'noAuthBlockingFunction',\n absentWhen: false,\n factProblems: takesNoFacts,\n build() {\n return [\n {\n title: 'no sign-in waits on a blocking function',\n description:\n 'No Functions source registers an Identity Platform blocking function (the identity module, or a v1 ' +\n 'beforeCreate / beforeSignIn / beforeEmail / beforeSms builder). Identity Platform refuses a sign-up or ' +\n 'sign-in whose blocking function has not answered within seconds, and a cold instance can take longer than ' +\n 'that to start, so a blocking function refuses members at random. Do the work after the fact instead.',\n run(context) {\n return context.files(FUNCTIONS_CODE).flatMap((file) =>\n blockingFunctionSites(file).map(({ node, detail }) => ({\n file: file.path,\n line: lineOfNode(file, node),\n detail,\n })),\n );\n },\n },\n ];\n },\n};\n\nexport interface AppCheckFacts {\n consumingFolders: readonly string[];\n adminAuth: { call: string; option: string };\n consumptionExempt: ReadonlyArray<{ callable: string; why: string }>;\n clientDeclarations: CallableDeclarationFacts;\n noClientDeclaration: ReadonlyArray<{ callable: string; why: string }>;\n}\n\ninterface Callable {\n name: string;\n node: ts.Node;\n options: ts.ObjectLiteralExpression | null;\n}\n\nconst isOnCall = (node: ts.Node): node is ts.CallExpression =>\n ts.isCallExpression(node) &&\n ts.isIdentifier(node.expression) &&\n node.expression.text === 'onCall';\n\nconst declaresTrue = (options: ts.ObjectLiteralExpression | null, key: string) =>\n !!options?.properties.some(\n (p) =>\n ts.isPropertyAssignment(p) &&\n p.name.getText().replace(/^['\"]|['\"]$/g, '') === key &&\n p.initializer.kind === ts.SyntaxKind.TrueKeyword,\n );\n\nexport function callablesOf(file: FileView): Callable[] {\n const sf = file.sourceFile;\n const optionsOf = (call: ts.CallExpression) =>\n call.arguments[0] && ts.isObjectLiteralExpression(call.arguments[0]) ? call.arguments[0] : null;\n const factories = new Map<string, ts.CallExpression>();\n const named = new Set<ts.CallExpression>();\n const out: Callable[] = [];\n forEachNode(sf, (node) => {\n if (ts.isFunctionDeclaration(node) && node.name && node.body) {\n forEachNode(node.body, (n) => {\n if (ts.isReturnStatement(n) && n.expression && isOnCall(n.expression))\n factories.set(node.name!.text, n.expression);\n });\n }\n });\n forEachNode(sf, (node) => {\n if (\n !ts.isVariableDeclaration(node) ||\n !ts.isIdentifier(node.name) ||\n !node.initializer ||\n !ts.isCallExpression(node.initializer)\n )\n return;\n const init: ts.CallExpression = node.initializer;\n if (isOnCall(init as ts.Node)) {\n named.add(init);\n out.push({ name: node.name.text, node: init, options: optionsOf(init) });\n } else if (ts.isIdentifier(init.expression) && factories.has(init.expression.text)) {\n const made = factories.get(init.expression.text)!;\n named.add(made);\n out.push({ name: node.name.text, node: init, options: optionsOf(made) });\n }\n });\n forEachNode(sf, (node) => {\n if (isOnCall(node) && !named.has(node))\n out.push({\n name: `(onCall at line ${lineOfNode(file, node)})`,\n node,\n options: optionsOf(node),\n });\n });\n return out;\n}\n\nexport function asksForAdmin(file: FileView, adminAuth: AppCheckFacts['adminAuth']): boolean {\n let found = false;\n forEachNode(file.sourceFile, (node) => {\n if (found || !ts.isCallExpression(node) || calleeName(node) !== adminAuth.call) return;\n found = node.arguments.some(\n (arg) =>\n ts.isObjectLiteralExpression(arg) &&\n arg.properties.some(\n (p) => p.name?.getText().replace(/^['\"]|['\"]$/g, '') === adminAuth.option,\n ),\n );\n });\n return found;\n}\n\nexport const appCheckGuard: GenericGuardDefinition<AppCheckFacts> = {\n id: 'appCheck',\n absentWhen: 'the app has no callable functions',\n factProblems(facts) {\n const problems: string[] = [];\n namesProblem(facts?.consumingFolders, 'consumingFolders', problems, { nonEmpty: false });\n textProblem(facts?.adminAuth?.call, 'adminAuth.call', problems);\n textProblem(facts?.adminAuth?.option, 'adminAuth.option', problems);\n reviewedProblems(facts?.consumptionExempt, 'consumptionExempt', problems, { key: 'callable' });\n declarationFactProblems(facts?.clientDeclarations, 'clientDeclarations', problems);\n reviewedProblems(facts?.noClientDeclaration, 'noClientDeclaration', problems, {\n key: 'callable',\n });\n return problems;\n },\n build(facts) {\n const posture: Guard = {\n title: 'every callable declares its App Check posture',\n description:\n 'Every `onCall` opens with a literal options object (one this check cannot read fails, never passes) that ' +\n 'declares `enforceAppCheck: true`: attestation sits beneath auth and neither substitutes for the other. A ' +\n `callable in a consuming folder (${facts.consumingFolders.join(', ') || 'none declared'}), and every callable in a ` +\n `file whose ${facts.adminAuth.call} asks for ${facts.adminAuth.option}, also declares \\`consumeAppCheckToken: true\\`, ` +\n 'so its attestation token is spent instead of reusable for its whole lifetime. A callable that keeps the cached ' +\n 'token is a reviewed exemption with its why; an exemption whose callable is gone or now consumes is stale.',\n run(context) {\n const findings: Finding[] = [];\n const exempt = new Map(facts.consumptionExempt.map((e) => [e.callable, e]));\n const exemptSeen = new Set<string>();\n const foldersWithCallables = new Set<string>();\n let callables = 0;\n for (const file of context.files(FUNCTIONS_CODE)) {\n const list = callablesOf(file);\n if (!list.length) continue;\n callables += list.length;\n const folder = facts.consumingFolders.find((f) => isUnder(file.path, f));\n if (folder) foldersWithCallables.add(folder);\n const mustConsume = !!folder || asksForAdmin(file, facts.adminAuth);\n for (const callable of list) {\n const line = lineOfNode(file, callable.node);\n if (!callable.options) {\n findings.push({\n file: file.path,\n line,\n detail: `${callable.name}: the onCall options are not an object literal, so the posture cannot be read`,\n });\n continue;\n }\n if (!declaresTrue(callable.options, 'enforceAppCheck'))\n findings.push({\n file: file.path,\n line,\n detail: `${callable.name}: no enforceAppCheck: true`,\n });\n const consumes = declaresTrue(callable.options, 'consumeAppCheckToken');\n if (exempt.has(callable.name)) {\n exemptSeen.add(callable.name);\n if (consumes)\n findings.push({\n file: file.path,\n line,\n detail: `stale exemption: ${callable.name} now consumes its token`,\n });\n else if (!mustConsume)\n findings.push({\n file: file.path,\n line,\n detail: `stale exemption: ${callable.name} is not a consuming callable`,\n });\n } else if (mustConsume && !consumes) {\n findings.push({\n file: file.path,\n line,\n detail: `${callable.name}: no consumeAppCheckToken: true`,\n });\n }\n }\n }\n if (!callables)\n findings.push({\n file: '(functions)',\n detail: 'no onCall found, so the guard checks nothing',\n });\n for (const folder of facts.consumingFolders)\n if (!foldersWithCallables.has(folder))\n findings.push({ file: folder, detail: 'stale consuming folder: it holds no callable' });\n for (const name of exempt.keys())\n if (!exemptSeen.has(name))\n findings.push({\n file: '(consumptionExempt)',\n detail: `stale exemption: no callable is named ${name}`,\n });\n return findings;\n },\n };\n const client: Guard = {\n title: \"a consuming callable's client declaration presents a limited-use token\",\n description:\n 'Every callable whose options declare `consumeAppCheckToken: true` has a client declaration in ' +\n `${facts.clientDeclarations.modules.join(', ')} carrying \\`limitedUseAppCheck: true\\`, so the token the server ` +\n 'spends is a single-use one rather than the cached token every other call reuses. A declaration carrying it names ' +\n 'a callable that consumes: one that names no callable, or a callable that keeps the cached token (an exempt one ' +\n 'included), mints a token per call for nothing. A consuming callable no client calls is a reviewed entry with ' +\n 'its why; an entry that is no longer a consuming callable, or that a declaration now names, is stale.',\n run(context) {\n const { declared, findings } = readDeclarations(context, facts.clientDeclarations);\n const servers = new Map<string, { consumes: boolean; file: string; line: number }>();\n for (const file of context.files(FUNCTIONS_CODE)) {\n for (const callable of callablesOf(file)) {\n servers.set(callable.name, {\n consumes: declaresTrue(callable.options, 'consumeAppCheckToken'),\n file: file.path,\n line: lineOfNode(file, callable.node),\n });\n }\n }\n if (!servers.size)\n findings.push({\n file: '(functions)',\n detail: 'no onCall found, so the guard checks nothing',\n });\n const byName = new Map(\n declared.filter((d) => d.callable !== null).map((d) => [d.callable!, d]),\n );\n const exempt = new Set(facts.consumptionExempt.map((e) => e.callable));\n const undeclared = new Set(facts.noClientDeclaration.map((e) => e.callable));\n for (const [name, server] of servers) {\n if (!server.consumes) continue;\n const declaration = byName.get(name);\n if (!declaration) {\n if (!undeclared.has(name))\n findings.push({\n file: server.file,\n line: server.line,\n detail: `${name} consumes its token, but no client declaration names it`,\n });\n } else if (!declaration.limitedUse) {\n findings.push({\n file: declaration.file,\n line: declaration.line,\n detail: `${declaration.hook} declares ${name}, which consumes its token, without limitedUseAppCheck: true`,\n });\n }\n }\n for (const declaration of declared) {\n if (!declaration.limitedUse || declaration.callable === null) continue;\n const server = servers.get(declaration.callable);\n const where = { file: declaration.file, line: declaration.line };\n if (!server)\n findings.push({\n ...where,\n detail: `${declaration.hook} declares ${declaration.callable} limited-use, but no callable of that name exists`,\n });\n else if (exempt.has(declaration.callable))\n findings.push({\n ...where,\n detail: `${declaration.hook} declares ${declaration.callable} limited-use, but it is exempt from consumption`,\n });\n else if (!server.consumes)\n findings.push({\n ...where,\n detail: `${declaration.hook} declares ${declaration.callable} limited-use, but it does not consume its token`,\n });\n }\n for (const name of undeclared) {\n if (byName.has(name))\n findings.push({\n file: '(noClientDeclaration)',\n detail: `stale entry: a client declaration names ${name} now`,\n });\n else if (!servers.get(name)?.consumes)\n findings.push({\n file: '(noClientDeclaration)',\n detail: `stale entry: ${name} is not a consuming callable`,\n });\n }\n return findings;\n },\n };\n return [posture, client];\n },\n};\n\nexport interface AdminActionSurfaceFacts {\n adminAuth: { call: string; option: string };\n adminCheckers: readonly string[];\n checkerReaders: readonly Reviewed[];\n adminDeclarations: CallableDeclarationFacts;\n claimKeys: readonly string[];\n adminHooks: readonly string[];\n adminAreaRoots: readonly string[];\n affordanceOwner: string;\n reviewed: ReadonlyArray<{ path: string; symbol?: string; why: string }>;\n importAliases: Readonly<Record<string, string>>;\n appServerZone: ServerZone;\n}\n\nconst APP_CLIENT_CODE: Scope = { roots: ['app'], kinds: ['code'], tests: false };\n\nfunction authCallsIn(root: ts.Node, adminAuth: AdminActionSurfaceFacts['adminAuth']) {\n const calls: Array<{ admin: boolean }> = [];\n forEachNode(root, (node) => {\n if (!ts.isCallExpression(node) || calleeName(node) !== adminAuth.call) return;\n calls.push({\n admin: node.arguments.some(\n (arg) =>\n ts.isObjectLiteralExpression(arg) &&\n arg.properties.some(\n (p) =>\n p.name?.getText().replace(/^['\"]|['\"]$/g, '') === adminAuth.option &&\n !(ts.isPropertyAssignment(p) && isOffLiteral(p.initializer)),\n ),\n ),\n });\n });\n return calls;\n}\n\nconst isOffLiteral = (value: ts.Expression): boolean =>\n value.kind === ts.SyntaxKind.FalseKeyword ||\n value.kind === ts.SyntaxKind.NullKeyword ||\n (ts.isIdentifier(value) && value.text === 'undefined');\n\nexport function adminOnlyCallables(\n file: FileView,\n adminAuth: AdminActionSurfaceFacts['adminAuth'],\n): string[] {\n const moduleCalls = authCallsIn(file.sourceFile, adminAuth);\n return callablesOf(file)\n .filter((callable) => {\n const own = authCallsIn(callable.node, adminAuth);\n const calls = own.length ? own : moduleCalls;\n return calls.length > 0 && calls.every((c) => c.admin);\n })\n .map((callable) => callable.name);\n}\n\ninterface ClientAdminUse {\n index: number;\n symbol: string;\n detail: string;\n}\n\nexport function clientAdminUses(\n file: FileView,\n facts: Pick<AdminActionSurfaceFacts, 'claimKeys' | 'adminHooks'>,\n declarationModuleOf: (spec: string) => string | null,\n): ClientAdminUse[] {\n const out: ClientAdminUse[] = [];\n const keys = new RegExp(`\\\\b(${facts.claimKeys.map(escapeRegExp).join('|')})\\\\b`);\n for (const hit of adminClaimMatcher(facts.claimKeys)(file))\n out.push({\n index: hit.index,\n symbol: keys.exec(hit.detail ?? '')?.[1] ?? '',\n detail: `reads the admin claim (${hit.detail})`,\n });\n const hooks = new Set(facts.adminHooks);\n for (const site of moduleImports(file.sourceFile, MODULE_LOADS)) {\n const index = site.node.getStart(file.sourceFile);\n const module = declarationModuleOf(site.spec);\n if (module) {\n const names = site.named.map((n) => n.imported);\n for (const symbol of names.length ? names : ['*'])\n out.push({\n index,\n symbol,\n detail: `imports ${symbol} from the admin declarations ${module}`,\n });\n continue;\n }\n for (const name of site.named)\n if (hooks.has(name.imported))\n out.push({\n index,\n symbol: name.imported,\n detail: `imports the admin hook ${name.imported}`,\n });\n }\n return out.sort((a, b) => a.index - b.index);\n}\n\nfunction adminAreaProblems(facts: AdminActionSurfaceFacts | undefined, problems: string[]): void {\n if (!Array.isArray(facts?.adminAreaRoots) || !facts.adminAreaRoots.length)\n problems.push('adminAreaRoots is missing');\n else facts.adminAreaRoots.forEach((r, i) => pathProblem(r, `adminAreaRoots[${i}]`, problems));\n pathProblem(facts?.affordanceOwner, 'affordanceOwner', problems);\n reviewedProblems(facts?.reviewed, 'reviewed', problems, {\n id: (e) =>\n typeof e.path === 'string' ? `${e.path}${isText(e.symbol) ? ` ${e.symbol}` : ''}` : undefined,\n extra(entry, label) {\n if (entry.symbol !== undefined && !isText(entry.symbol))\n problems.push(`${label}'s symbol is empty`);\n },\n });\n}\n\nexport const adminActionSurfaceGuard: GenericGuardDefinition<AdminActionSurfaceFacts> = {\n id: 'adminActionSurface',\n absentWhen: 'the app has no admin actions',\n factProblems(facts) {\n const problems: string[] = [];\n if (objectProblem(facts?.adminAuth, 'adminAuth', problems)) {\n textProblem(facts.adminAuth.call, 'adminAuth.call', problems);\n textProblem(facts.adminAuth.option, 'adminAuth.option', problems);\n }\n namesProblem(facts?.adminCheckers, 'adminCheckers', problems);\n reviewedProblems(facts?.checkerReaders, 'checkerReaders', problems);\n declarationFactProblems(facts?.adminDeclarations, 'adminDeclarations', problems);\n namesProblem(facts?.claimKeys, 'claimKeys', problems);\n namesProblem(facts?.adminHooks, 'adminHooks', problems, { nonEmpty: false });\n adminAreaProblems(facts, problems);\n objectProblem(facts?.importAliases, 'importAliases', problems);\n serverZoneProblems(facts?.appServerZone, 'appServerZone', problems);\n return problems;\n },\n build(facts) {\n const declarationModules = new Set(facts.adminDeclarations.modules);\n const probePath = `${facts.affordanceOwner.slice(0, facts.affordanceOwner.lastIndexOf('/') + 1)}probe.tsx`;\n return [\n {\n title: 'admin actions outside the admin area go through the admin-actions affordance',\n description:\n `The admin-only callables are every callable whose ${facts.adminAuth.call} calls all carry ` +\n `${facts.adminAuth.option}, read from the Functions source each run. Inside a callable module, admin standing ` +\n `is checked only through that auth call: ${facts.adminCheckers.join(', ')} is called only by the reviewed ` +\n `readers. ${facts.adminDeclarations.modules.join(', ')} declares exactly the admin-only callables, no more and ` +\n `no fewer. Outside the admin area (${facts.adminAreaRoots.join(', ')}) and the admin-actions affordance ` +\n `(${facts.affordanceOwner}), client code never reads the admin claim, imports an admin declaration, or imports ` +\n 'an admin hook: an admin action there goes through the affordance, and any other admin-standing read is a ' +\n 'reviewed entry ({ path, symbol?, why }). A reviewed reader or entry that no longer matches is stale.',\n samples: {\n flags: [\n { path: probePath, text: `if (claims?.${facts.claimKeys[0]}) show();` },\n ...facts.adminHooks\n .slice(0, 1)\n .map((hook) => ({ path: probePath, text: `import { ${hook} } from './hooks';` })),\n ],\n passes: [\n { path: probePath, text: `const label = '${facts.claimKeys[0]}';` },\n { path: probePath, text: \"import { useRows } from './hooks';\" },\n ],\n probe: (view) => clientAdminUses(view, facts, () => null),\n },\n run(context) {\n const findings: Finding[] = [];\n const adminCallables = new Map<string, { file: string; line: number }>();\n const readers = new Map(facts.checkerReaders.map((r) => [r.path, false]));\n const checkers = new Set(facts.adminCheckers);\n for (const file of context.files(FUNCTIONS_CODE)) {\n const callables = callablesOf(file);\n if (!callables.length) continue;\n for (const name of adminOnlyCallables(file, facts.adminAuth)) {\n const callable = callables.find((c) => c.name === name)!;\n adminCallables.set(name, { file: file.path, line: lineOfNode(file, callable.node) });\n }\n forEachNode(file.sourceFile, (node) => {\n if (!ts.isCallExpression(node)) return;\n const name = calleeName(node);\n if (!name || !checkers.has(name)) return;\n if (readers.has(file.path)) readers.set(file.path, true);\n else\n findings.push({\n file: file.path,\n line: lineOfNode(file, node),\n detail: `a callable module checks admin standing through ${name} instead of its ${facts.adminAuth.call}`,\n });\n });\n }\n for (const [path, used] of readers)\n if (!used)\n findings.push({\n file: path,\n detail: 'stale reader: it no longer calls an admin checker in a callable module',\n });\n if (!adminCallables.size)\n findings.push({\n file: '(functions)',\n detail: `no callable asks ${facts.adminAuth.call} for ${facts.adminAuth.option}, so no admin-only callable was found`,\n });\n\n const { declared, findings: declarationFindings } = readDeclarations(\n context,\n facts.adminDeclarations,\n );\n findings.push(...declarationFindings);\n const declaredNames = new Set<string>();\n for (const d of declared) {\n if (d.callable === null) continue;\n declaredNames.add(d.callable);\n if (!adminCallables.has(d.callable))\n findings.push({\n file: d.file,\n line: d.line,\n detail: `${d.hook} declares ${d.callable}, which is not an admin-only callable`,\n });\n }\n for (const [name, where] of adminCallables)\n if (!declaredNames.has(name))\n findings.push({\n ...where,\n detail: `${name} is admin-only, but ${facts.adminDeclarations.modules.join(', ')} does not declare it`,\n });\n\n for (const root of facts.adminAreaRoots)\n if (!context.reader.exists(root))\n findings.push({ file: root, detail: 'stale admin-area root: it no longer exists' });\n const ownerExists = context.reader.exists(facts.affordanceOwner);\n if (!ownerExists)\n findings.push({\n file: facts.affordanceOwner,\n detail: 'the admin-actions affordance no longer exists',\n });\n const aliases = aliasList(facts.importAliases);\n const reviewedKey = (e: { path: string; symbol?: string }) =>\n `${e.path}${e.symbol ? ` ${e.symbol}` : ''}`;\n const reviewedSeen = new Set<string>();\n let ownerHolds = false;\n for (const file of context.files(APP_CLIENT_CODE)) {\n if (inServerZone(file, facts.appServerZone)) continue;\n if (facts.adminAreaRoots.some((root) => isUnder(file.path, root))) continue;\n const uses = clientAdminUses(file, facts, (spec) => {\n const resolved = resolveSpecifier(context.reader, file.path, spec, aliases);\n return declarationModules.has(resolved) ? resolved : null;\n });\n if (file.path === facts.affordanceOwner) {\n ownerHolds ||= uses.length > 0;\n continue;\n }\n for (const use of uses) {\n const entry = facts.reviewed.find(\n (e) => e.path === file.path && (e.symbol === undefined || e.symbol === use.symbol),\n );\n if (entry) reviewedSeen.add(reviewedKey(entry));\n else\n findings.push({\n file: file.path,\n line: file.lineOf(use.index),\n detail: `outside the admin area: ${use.detail}`,\n });\n }\n }\n if (ownerExists && !ownerHolds)\n findings.push({\n file: facts.affordanceOwner,\n detail:\n 'stale affordance owner: it reads no admin standing and imports no admin declaration or hook',\n });\n for (const entry of facts.reviewed)\n if (!reviewedSeen.has(reviewedKey(entry)))\n findings.push({\n file: entry.path,\n detail: `stale entry: it no longer reads admin standing${entry.symbol ? ` through ${entry.symbol}` : ''}`,\n });\n return findings;\n },\n },\n ];\n },\n};\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"functions-build.d.ts","sourceRoot":"","sources":["../../../src/modules/backend/functions-build.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"functions-build.d.ts","sourceRoot":"","sources":["../../../src/modules/backend/functions-build.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { posix } from 'node:path';
|
|
1
2
|
import ts from 'typescript';
|
|
2
3
|
import { ROOT_ROLES } from '../../node/layout.js';
|
|
3
4
|
import { calleeName, forEachNode, lineOfNode } from '../../support/ast.js';
|
|
@@ -13,7 +14,7 @@ function functionsRoot(context) {
|
|
|
13
14
|
throw new Error('repo-guards: a Functions guard runs and the layout declares no functions root');
|
|
14
15
|
return root.path;
|
|
15
16
|
}
|
|
16
|
-
const moduleIdOf = (root,
|
|
17
|
+
const moduleIdOf = (root, file) => posix.relative(root, file).replace(CODE_EXTENSION, '');
|
|
17
18
|
const isMapAccess = (node, map) => ts.isPropertyAccessExpression(node) &&
|
|
18
19
|
ts.isIdentifier(node.expression) &&
|
|
19
20
|
node.expression.text === map;
|