@ttt-productions/repo-guards 0.1.2 → 0.1.3

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.
@@ -27,6 +27,16 @@ export interface MonitoringFacts {
27
27
  sdkInitFiles: readonly Reviewed[];
28
28
  /** The Functions test seam that records captures; tests use it instead of mocking the package. */
29
29
  functionsTestRecorder: string;
30
+ /**
31
+ * Entry points whose failures their core reports: the entry file, the core module it hands every
32
+ * event to, and the why (`[]` when there is none). An entry is stale when the core no longer
33
+ * captures, the entry file no longer imports it, or the entry file reports on its own.
34
+ */
35
+ reportedByCore: ReadonlyArray<{
36
+ path: string;
37
+ core: string;
38
+ why: string;
39
+ }>;
30
40
  }
31
41
  /** The entry-point shapes a file defines, or none when its failures reach monitoring. */
32
42
  export declare function unmonitoredEntryPoint(file: FileView, facts: Pick<MonitoringFacts, 'monitoringPackage' | 'captureCalls' | 'callableExits'>): string[];
@@ -1 +1 @@
1
- {"version":3,"file":"monitoring.d.ts","sourceRoot":"","sources":["../../../src/modules/backend/monitoring.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"monitoring.d.ts","sourceRoot":"","sources":["../../../src/modules/backend/monitoring.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,7 @@ import ts from 'typescript';
6
6
  import { calleeName, forEachNode, lineOfNode } from '../../support/ast.js';
7
7
  import { loaderProblem, namesProblem, reviewedProblems, textProblem } from '../../support/facts.js';
8
8
  import { MODULE_LOADS, moduleImports, namesModule } from '../../support/imports.js';
9
+ import { resolveRelative } from '../../support/paths.js';
9
10
  import { escapeRegExp } from '../../support/text.js';
10
11
  const FUNCTIONS_CODE = { roots: ['functions'], kinds: ['code'], tests: false };
11
12
  const APP_CODE = { roots: ['app'], kinds: ['code'], tests: false };
@@ -22,6 +23,23 @@ function matchIndexes(file, pattern) {
22
23
  out.push(m.index);
23
24
  return out;
24
25
  }
26
+ /** A module path without its source or emitted extension, for comparing an import to a file. */
27
+ const withoutExtension = (path) => path.replace(/\.(?:[cm]?[jt]sx?)$/, '');
28
+ /** True when a file imports the module at `corePath` for its values. */
29
+ function importsModule(file, corePath) {
30
+ const target = withoutExtension(corePath);
31
+ return file.imports.some(({ spec, typeOnly }) => {
32
+ if (typeOnly)
33
+ return false;
34
+ const resolved = resolveRelative(file.path, spec);
35
+ return resolved !== null && !resolved.escapes && withoutExtension(resolved.path) === target;
36
+ });
37
+ }
38
+ /** True when a file imports the monitoring package and calls one of its capture calls. */
39
+ function reportsToMonitoring(file, facts) {
40
+ const importsMonitoring = moduleImports(file.sourceFile, MODULE_LOADS).some(({ spec, typeOnly }) => !typeOnly && namesModule(spec, facts.monitoringPackage));
41
+ return importsMonitoring && facts.captureCalls.some((capture) => new RegExp(`\\b${escapeRegExp(capture)}\\s*\\(`).test(file.code));
42
+ }
25
43
  /** Every Cloud Functions entry-point shape, with the label a finding reports. */
26
44
  const ENTRY_POINT_SHAPES = [
27
45
  { shape: 'onCall', re: /\bonCall\s*(?:<[^>]*>)?\s*\(/ },
@@ -41,9 +59,7 @@ export function unmonitoredEntryPoint(file, facts) {
41
59
  return [];
42
60
  if (facts.callableExits.some((exit) => callPattern(exit).test(file.code)))
43
61
  return [];
44
- const importsMonitoring = moduleImports(file.sourceFile, MODULE_LOADS).some(({ spec, typeOnly }) => !typeOnly && namesModule(spec, facts.monitoringPackage));
45
- const captures = facts.captureCalls.some((capture) => new RegExp(`\\b${escapeRegExp(capture)}\\s*\\(`).test(file.code));
46
- return importsMonitoring && captures ? [] : shapes;
62
+ return reportsToMonitoring(file, facts) ? [] : shapes;
47
63
  }
48
64
  /** Every import of a provider SDK package. */
49
65
  export function providerSdkImports(file, sdks) {
@@ -79,6 +95,12 @@ export const monitoringGuard = {
79
95
  textProblem(facts?.providerInitCall, 'providerInitCall', problems);
80
96
  reviewedProblems(facts?.sdkInitFiles, 'sdkInitFiles', problems);
81
97
  textProblem(facts?.functionsTestRecorder, 'functionsTestRecorder', problems);
98
+ if (!Array.isArray(facts?.reportedByCore))
99
+ problems.push('reportedByCore is missing (declare [] when every entry point reports its own failures)');
100
+ for (const entry of facts?.reportedByCore ?? []) {
101
+ if (!entry?.path || !entry.core || !entry.why?.trim())
102
+ problems.push('a reportedByCore entry needs its entry file, its core module, and its why');
103
+ }
82
104
  return problems;
83
105
  },
84
106
  build(facts) {
@@ -90,13 +112,38 @@ export const monitoringGuard = {
90
112
  `or database trigger — either exits its callable through ${facts.callableExits.join(' / ') || 'the app\'s callable exit'} ` +
91
113
  `or imports ${facts.monitoringPackage} and calls ${facts.captureCalls.join(' / ')} on its failure path. A failure ` +
92
114
  'path that only writes a log line is not monitored: nothing watches the log, so a background trigger fails ' +
93
- 'invisibly. An import that is never called is not monitoring either.',
115
+ 'invisibly. An import that is never called is not monitoring either. An entry point whose core reports every ' +
116
+ 'failure is a reviewed entry naming that core, with its why; it is stale when the core no longer captures, ' +
117
+ 'the entry point no longer imports it, or the entry point reports on its own.',
94
118
  run(context) {
95
119
  const findings = [];
120
+ const reviewed = new Map(facts.reportedByCore.map((entry) => [entry.path, entry]));
121
+ const seen = new Set();
96
122
  for (const file of context.files(FUNCTIONS_CODE)) {
97
123
  const shapes = unmonitoredEntryPoint(file, facts);
98
- if (shapes.length)
99
- findings.push({ file: file.path, detail: `defines ${shapes.join(', ')} and reports no failure to monitoring` });
124
+ const entry = reviewed.get(file.path);
125
+ if (!entry) {
126
+ if (shapes.length)
127
+ findings.push({ file: file.path, detail: `defines ${shapes.join(', ')} and reports no failure to monitoring` });
128
+ continue;
129
+ }
130
+ seen.add(file.path);
131
+ if (!ENTRY_POINT_SHAPES.some(({ re }) => re.test(file.code))) {
132
+ findings.push({ file: file.path, detail: 'a reportedByCore entry for a file that defines no entry point' });
133
+ }
134
+ else if (!shapes.length) {
135
+ findings.push({ file: file.path, detail: 'a reportedByCore entry for an entry point that reports on its own' });
136
+ }
137
+ else if (!importsModule(file, entry.core)) {
138
+ findings.push({ file: file.path, detail: `a reportedByCore entry names ${entry.core}, which the entry point does not import` });
139
+ }
140
+ else if (!context.reader.exists(entry.core) || !reportsToMonitoring(context.file(entry.core), facts)) {
141
+ findings.push({ file: file.path, detail: `a reportedByCore entry names ${entry.core}, which reports no failure to monitoring` });
142
+ }
143
+ }
144
+ for (const entry of facts.reportedByCore) {
145
+ if (!seen.has(entry.path))
146
+ findings.push({ file: entry.path, detail: 'a reportedByCore entry for a file the Functions code does not hold' });
100
147
  }
101
148
  return findings;
102
149
  },
@@ -1 +1 @@
1
- {"version":3,"file":"monitoring.js","sourceRoot":"","sources":["../../../src/modules/backend/monitoring.ts"],"names":[],"mappings":"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","sourcesContent":["// The monitoring guards: every Cloud Function entry point reports its failures, monitoring is bound\n// once per runtime in its adapters, the provider's SDK is named only where it starts, tests capture\n// through the recording seam, telemetry tags survive the content policy, and a service the app does\n// not use is named nowhere.\n\nimport ts from 'typescript';\nimport type { GenericGuardDefinition } from '../../config.js';\nimport type { Finding, Guard } from '../../guard.js';\nimport type { Scope } from '../../reader/reader.js';\nimport type { FileView } from '../../reader/view.js';\nimport type { Reviewed } from '../../reader/layout.js';\nimport { calleeName, forEachNode, lineOfNode } from '../../support/ast.js';\nimport { loaderProblem, namesProblem, reviewedProblems, textProblem } from '../../support/facts.js';\nimport { MODULE_LOADS, moduleImports, namesModule } from '../../support/imports.js';\nimport { escapeRegExp } from '../../support/text.js';\n\nconst FUNCTIONS_CODE: Scope = { roots: ['functions'], kinds: ['code'], tests: false };\nconst APP_CODE: Scope = { roots: ['app'], kinds: ['code'], tests: false };\nconst APP_AND_FUNCTIONS_CODE: Scope = { roots: ['app', 'functions'], kinds: ['code'], tests: false };\n\n/** A pattern for a call to `name` (`a.b` matches `a . b (`), never a longer name ending in it. */\nfunction callPattern(name: string): RegExp {\n return new RegExp(`(?<![\\\\w$])${name.split('.').map(escapeRegExp).join('\\\\s*\\\\.\\\\s*')}\\\\s*\\\\(`, 'g');\n}\n\n/** The index of every match of a pattern in a file's comment-stripped code. */\nfunction matchIndexes(file: FileView, pattern: RegExp): number[] {\n const re = new RegExp(pattern.source, pattern.flags.includes('g') ? pattern.flags : `${pattern.flags}g`);\n const out: number[] = [];\n for (let m = re.exec(file.code); m; m = re.exec(file.code)) out.push(m.index);\n return out;\n}\n\n// ---------------------------------------------------------------------------------------------\n// Monitoring: entry points, one init per runtime, the provider SDK, the recording seam.\n\nexport interface MonitoringFacts {\n /** The one monitoring package every capture goes through. */\n monitoringPackage: string;\n /** The package's init call, which binds the provider. */\n initCall: string;\n /** The package's capture calls. */\n captureCalls: readonly string[];\n /** App calls through which a callable exits, each capturing every fault. */\n callableExits: readonly string[];\n /** The one Functions module that initializes monitoring. */\n functionsInit: string;\n /** The app root's reporter bindings: the file that initializes, and the layout that mounts it as an element. */\n appReporters: ReadonlyArray<{ file: string; mountedIn: string; element: string }>;\n /** The provider SDK's package names (or scope, `@vendor`), named only where the SDK starts. */\n providerSdks: readonly string[];\n /** The provider SDK's own init call (`Vendor.init`). */\n providerInitCall: string;\n /** The files that start the provider SDK, each with its why; one that no longer starts it is stale. */\n sdkInitFiles: readonly Reviewed[];\n /** The Functions test seam that records captures; tests use it instead of mocking the package. */\n functionsTestRecorder: string;\n}\n\n/** Every Cloud Functions entry-point shape, with the label a finding reports. */\nconst ENTRY_POINT_SHAPES: ReadonlyArray<{ shape: string; re: RegExp }> = [\n { shape: 'onCall', re: /\\bonCall\\s*(?:<[^>]*>)?\\s*\\(/ },\n { shape: 'onRequest', re: /\\bonRequest\\s*\\(/ },\n { shape: 'onSchedule', re: /\\bonSchedule\\s*\\(/ },\n { shape: 'onDocument*', re: /\\bonDocument\\w*\\s*\\(/ },\n { shape: 'onObjectFinalized', re: /\\bonObjectFinalized\\s*\\(/ },\n { shape: 'onObjectDeleted', re: /\\bonObjectDeleted\\s*\\(/ },\n { shape: 'onMessagePublished', re: /\\bonMessagePublished\\s*\\(/ },\n { shape: 'onTaskDispatched', re: /\\bonTaskDispatched\\s*\\(/ },\n { shape: 'onValue*', re: /\\bonValue\\w*\\s*\\(/ },\n];\n\n/** The entry-point shapes a file defines, or none when its failures reach monitoring. */\nexport function unmonitoredEntryPoint(file: FileView, facts: Pick<MonitoringFacts, 'monitoringPackage' | 'captureCalls' | 'callableExits'>): string[] {\n const shapes = ENTRY_POINT_SHAPES.filter(({ re }) => re.test(file.code)).map(({ shape }) => shape);\n if (!shapes.length) return [];\n if (facts.callableExits.some((exit) => callPattern(exit).test(file.code))) return [];\n const importsMonitoring = moduleImports(file.sourceFile, MODULE_LOADS).some(({ spec, typeOnly }) => !typeOnly && namesModule(spec, facts.monitoringPackage));\n const captures = facts.captureCalls.some((capture) => new RegExp(`\\\\b${escapeRegExp(capture)}\\\\s*\\\\(`).test(file.code));\n return importsMonitoring && captures ? [] : shapes;\n}\n\n/** Every import of a provider SDK package. */\nexport function providerSdkImports(file: FileView, sdks: readonly string[]): Array<{ node: ts.Node; spec: string }> {\n return moduleImports(file.sourceFile, MODULE_LOADS)\n .filter(({ spec }) => sdks.some((sdk) => namesModule(spec, sdk)))\n .map(({ node, spec }) => ({ node, spec }));\n}\n\n/** Every `vi.mock` / `vi.doMock` of a package. */\nexport function packageMocks(file: FileView, pkg: string): ts.Node[] {\n return moduleImports(file.sourceFile, ['mock'])\n .filter(({ callee, spec }) => (callee === 'vi.mock' || callee === 'vi.doMock') && namesModule(spec, pkg))\n .map(({ node }) => node);\n}\n\n/** True when a file renders `<Element />` (or `<Element>…`). */\nconst mountsElement = (file: FileView, element: string) => new RegExp(`<${escapeRegExp(element)}(?=[\\\\s/>])`).test(file.code);\n\nexport const monitoringGuard: GenericGuardDefinition<MonitoringFacts> = {\n id: 'monitoring',\n absentWhen: false,\n factProblems(facts) {\n const problems: string[] = [];\n textProblem(facts?.monitoringPackage, 'monitoringPackage', problems);\n textProblem(facts?.initCall, 'initCall', problems);\n namesProblem(facts?.captureCalls, 'captureCalls', problems);\n namesProblem(facts?.callableExits, 'callableExits', problems, { nonEmpty: false });\n textProblem(facts?.functionsInit, 'functionsInit', problems);\n if (!Array.isArray(facts?.appReporters)) problems.push('appReporters is missing (declare [] when the app root binds no reporter)');\n for (const r of facts?.appReporters ?? []) {\n if (!r?.file || !r.mountedIn || !r.element) problems.push('an app reporter needs its file, the layout that mounts it, and the element');\n }\n namesProblem(facts?.providerSdks, 'providerSdks', problems);\n textProblem(facts?.providerInitCall, 'providerInitCall', problems);\n reviewedProblems(facts?.sdkInitFiles, 'sdkInitFiles', problems);\n textProblem(facts?.functionsTestRecorder, 'functionsTestRecorder', problems);\n return problems;\n },\n build(facts) {\n const initPattern = callPattern(facts.initCall);\n const providerInit = callPattern(facts.providerInitCall);\n const entryPoints: Guard = {\n title: 'every Cloud Function entry point reports its failures to monitoring',\n description:\n 'Every file that defines a deployed Cloud Function — callable, HTTP, schedule, Firestore, Storage, pub/sub, task, ' +\n `or database trigger — either exits its callable through ${facts.callableExits.join(' / ') || 'the app\\'s callable exit'} ` +\n `or imports ${facts.monitoringPackage} and calls ${facts.captureCalls.join(' / ')} on its failure path. A failure ` +\n 'path that only writes a log line is not monitored: nothing watches the log, so a background trigger fails ' +\n 'invisibly. An import that is never called is not monitoring either.',\n run(context) {\n const findings: Finding[] = [];\n for (const file of context.files(FUNCTIONS_CODE)) {\n const shapes = unmonitoredEntryPoint(file, facts);\n if (shapes.length) findings.push({ file: file.path, detail: `defines ${shapes.join(', ')} and reports no failure to monitoring` });\n }\n return findings;\n },\n };\n const initOnce: Guard = {\n title: 'monitoring is initialized once per runtime, only in its adapters',\n description:\n `The Functions runtime initializes monitoring in ${facts.functionsInit} and nowhere else; the app root binds its ` +\n `reporter only in its declared adapters, each initializing once and mounted by its layout. A second ` +\n `${facts.initCall} is a second configuration nothing keeps aligned with the first and double-reports the moment ` +\n 'both run; a missing one leaves every capture writing into an unbound client. Pass the values at the existing ' +\n 'adapter instead of opening a new init.',\n run(context) {\n const findings: Finding[] = [];\n let functionsInits = 0;\n for (const file of context.files(FUNCTIONS_CODE)) {\n const sites = matchIndexes(file, initPattern);\n if (file.path === facts.functionsInit) functionsInits += sites.length;\n else for (const i of sites) findings.push({ file: file.path, line: file.lineOf(i), detail: `${facts.initCall} outside the Functions adapter` });\n }\n if (functionsInits !== 1) findings.push({ file: facts.functionsInit, detail: `the Functions adapter calls ${facts.initCall} ${functionsInits} times, not once` });\n const reporters = new Map(facts.appReporters.map((r) => [r.file, r]));\n const reporterInits = new Map<string, number>();\n for (const file of context.files(APP_CODE)) {\n const sites = matchIndexes(file, initPattern);\n if (reporters.has(file.path)) reporterInits.set(file.path, sites.length);\n else for (const i of sites) findings.push({ file: file.path, line: file.lineOf(i), detail: `${facts.initCall} outside the app's reporter adapters` });\n }\n for (const reporter of facts.appReporters) {\n const count = reporterInits.get(reporter.file) ?? 0;\n if (count !== 1) findings.push({ file: reporter.file, detail: `the reporter adapter calls ${facts.initCall} ${count} times, not once` });\n if (!context.reader.exists(reporter.mountedIn)) findings.push({ file: reporter.mountedIn, detail: 'the layout that mounts the reporter no longer exists' });\n else if (!mountsElement(context.file(reporter.mountedIn), reporter.element)) {\n findings.push({ file: reporter.mountedIn, detail: `does not mount <${reporter.element} />, so the reporter is never bound` });\n }\n }\n return findings;\n },\n };\n const providerNamed: Guard = {\n title: 'the monitoring provider SDK is named only where it starts',\n description:\n `No file in the app or Functions roots imports a provider SDK package (${facts.providerSdks.join(', ')}) or calls ` +\n `${facts.providerInitCall}: the provider binds inside ${facts.monitoringPackage}, and a direct import is a third ` +\n 'binding site whose DSN, environment, and sample rate the app boundary can no longer supply. Capture through the ' +\n 'monitoring package instead. Each declared SDK init file still starts the SDK; one that no longer does is stale.',\n run(context) {\n const findings: Finding[] = [];\n for (const file of context.files(APP_AND_FUNCTIONS_CODE)) {\n for (const { node, spec } of providerSdkImports(file, facts.providerSdks)) {\n findings.push({ file: file.path, line: lineOfNode(file, node), detail: `imports ${spec}` });\n }\n for (const i of matchIndexes(file, providerInit)) findings.push({ file: file.path, line: file.lineOf(i), detail: `calls ${facts.providerInitCall}` });\n }\n for (const entry of facts.sdkInitFiles) {\n if (!context.reader.exists(entry.path)) findings.push({ file: entry.path, detail: 'stale SDK init file: it no longer exists' });\n else if (!matchIndexes(context.file(entry.path), providerInit).length) {\n findings.push({ file: entry.path, detail: `stale SDK init file: it no longer calls ${facts.providerInitCall}` });\n }\n }\n return findings;\n },\n };\n const recorderSeam: Guard = {\n title: 'Functions tests capture through the recording seam, never a local mock of the monitoring package',\n description:\n `No Functions test replaces ${facts.monitoringPackage} with a local vi.mock / vi.doMock. A test that asserts a ` +\n `capture installs the recording adapter (${facts.functionsTestRecorder}) through the package's own adapter seam; a ` +\n 'test that only needs the handler to run needs no mock at all. A local mock lists only the exports its author ' +\n 'knew, and a stubbed scope wrapper never runs the wrapped handler.',\n run(context) {\n const findings: Finding[] = [];\n let seam = false;\n for (const file of context.files({ roots: ['functions'], kinds: ['code'], tests: 'only' })) {\n if (file.path === facts.functionsTestRecorder) seam = true;\n for (const node of packageMocks(file, facts.monitoringPackage)) {\n findings.push({ file: file.path, line: lineOfNode(file, node), detail: `mocks ${facts.monitoringPackage}` });\n }\n }\n if (!seam) findings.push({ file: facts.functionsTestRecorder, detail: 'the recording seam is gone or is not test code' });\n return findings;\n },\n };\n return [entryPoints, initOnce, providerNamed, recorderSeam];\n },\n};\n\n// ---------------------------------------------------------------------------------------------\n// Telemetry tag keys survive the content policy.\n\nexport interface TelemetryTagFacts {\n /** The app's telemetry content policy, as its monitoring package builds it, loaded at run time. */\n policy: () => Promise<{ isDiagnosticKey(key: string): boolean }> | { isDiagnosticKey(key: string): boolean };\n /** Tag keys whose value is always a flag, which passes under any key; a key set nowhere is stale. */\n flagKeys: ReadonlyArray<{ key: string; why: string }>;\n /** The name suffix of a constant whose values are tag keys (`_TAG_KEYS`). */\n tagKeyMapSuffix: string;\n}\n\n/** Every tag key a file sets: a `tags: { … }` literal's keys, `setTag('key', …)`, and a tag-key map's values. */\nexport function tagKeysSet(file: FileView, mapSuffix: string): Array<{ key: string; node: ts.Node }> {\n const sf = file.sourceFile;\n const out: Array<{ key: string; node: ts.Node }> = [];\n const keyOf = (name: ts.PropertyName) => (ts.isIdentifier(name) || ts.isStringLiteralLike(name) ? name.text : undefined);\n forEachNode(sf, (node) => {\n if (ts.isPropertyAssignment(node) && keyOf(node.name) === 'tags' && ts.isObjectLiteralExpression(node.initializer)) {\n for (const prop of node.initializer.properties) {\n const key = (ts.isPropertyAssignment(prop) || ts.isShorthandPropertyAssignment(prop)) && keyOf(prop.name);\n if (key) out.push({ key, node: prop });\n }\n }\n if (ts.isCallExpression(node) && calleeName(node) === 'setTag' && node.arguments[0] && ts.isStringLiteralLike(node.arguments[0])) {\n out.push({ key: node.arguments[0].text, node });\n }\n if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.name.text.endsWith(mapSuffix) && node.initializer) {\n let init: ts.Expression = node.initializer;\n while (ts.isAsExpression(init) || ts.isSatisfiesExpression(init)) init = init.expression;\n if (ts.isObjectLiteralExpression(init)) {\n for (const prop of init.properties) {\n if (ts.isPropertyAssignment(prop) && ts.isStringLiteralLike(prop.initializer)) out.push({ key: prop.initializer.text, node: prop });\n }\n }\n }\n });\n return out;\n}\n\nexport const telemetryTagGuard: GenericGuardDefinition<TelemetryTagFacts> = {\n id: 'telemetryTagKeys',\n absentWhen: 'the app declares no telemetry content policy',\n factProblems(facts) {\n const problems: string[] = [];\n loaderProblem(facts?.policy, 'policy', problems);\n reviewedProblems(facts?.flagKeys, 'flagKeys', problems, { key: 'key' });\n textProblem(facts?.tagKeyMapSuffix, 'tagKeyMapSuffix', problems);\n return problems;\n },\n build(facts) {\n return [\n {\n title: 'every backend telemetry tag key survives the content policy',\n description:\n 'A tag the backend sets survives the telemetry content policy only under a diagnostic key, so every tag key a ' +\n \"capture site sets — a `tags: { … }` literal's keys, a `setTag('key', …)`, a tag-key map's values — is one; \" +\n 'otherwise the policy silently drops it. Rename the tag to a diagnostic key, or widen the policy only to a key ' +\n 'that never holds prose. A key whose value is always a flag is a reviewed entry with its why.',\n async run(context) {\n const policy = await facts.policy();\n const flags = new Set(facts.flagKeys.map((f) => f.key));\n const findings: Finding[] = [];\n const seen = new Set<string>();\n for (const file of context.files(FUNCTIONS_CODE)) {\n for (const { key, node } of tagKeysSet(file, facts.tagKeyMapSuffix)) {\n seen.add(key);\n if (!flags.has(key) && !policy.isDiagnosticKey(key)) {\n findings.push({ file: file.path, line: lineOfNode(file, node), detail: `tag key ${key} is not a diagnostic key, so the policy drops it` });\n }\n }\n }\n if (!seen.size) findings.push({ file: '(functions)', detail: 'no tag key found, so the guard checks nothing' });\n for (const key of flags) if (!seen.has(key)) findings.push({ file: '(flagKeys)', detail: `stale flag key ${key}: no capture site sets it` });\n return findings;\n },\n },\n ];\n },\n};\n\n// ---------------------------------------------------------------------------------------------\n// A service the app does not use is named nowhere.\n\nexport interface UnusedServiceFacts {\n /** Services the product has no surface for, each with its why. */\n services: ReadonlyArray<{ name: string; why: string }>;\n /** Text that contains a service's name but is not the service (a word it is part of), each with its why. */\n collisions: ReadonlyArray<{ text: string; why: string }>;\n /** Files outside the roots read by name: manifests, deployed configuration, workflows, rules, index files. */\n namedFiles: readonly string[];\n}\n\nconst SERVICE_SCOPE: Scope = {\n roots: ['app', 'functions', 'e2e', 'hostedE2e', 'scripts', 'workers'],\n optionalRoots: ['functions', 'e2e', 'hostedE2e', 'scripts', 'workers'],\n kinds: ['code', 'config', 'rules'],\n tests: true,\n};\n\n/**\n * Where a service's name appears as a name: its lowercase form starting a word and not running on\n * into lowercase letters (`svc`, `svcSessionId`, `svc.`, `'svc'`, `@svc/`); its capitalized form\n * anywhere not running on (`Svc`, `loadSvc`); its uppercase form standing alone between non-letters\n * (`SVC_KEY`). A longer word that merely contains it (`svced`, `pinsvc`) is not the service.\n */\nexport function serviceNamePatterns(name: string): RegExp[] {\n const lower = escapeRegExp(name.toLowerCase());\n const cap = escapeRegExp(name.charAt(0).toUpperCase() + name.slice(1).toLowerCase());\n const upper = escapeRegExp(name.toUpperCase());\n return [\n new RegExp(`(?<![A-Za-z0-9])${lower}(?![a-z])`, 'g'),\n new RegExp(`${cap}(?![a-z])`, 'g'),\n new RegExp(`(?<![A-Z0-9])${upper}(?![A-Z0-9])`, 'g'),\n ];\n}\n\n/** The lines of a file's text a reader of the service name reads: comments skipped by line. */\nfunction readableLines(file: FileView): string[] {\n const lines = file.code.split('\\n');\n const name = file.path.slice(file.path.lastIndexOf('/') + 1);\n if (file.kind === 'config' && !/\\.jsonc?$/.test(name)) return lines.map((l) => (/^\\s*#/.test(l) ? '' : l));\n if (file.kind === 'rules') return lines.map((l) => (/^\\s*\\/\\//.test(l) ? '' : l));\n return lines;\n}\n\n/** Every place a file names a service, collisions taken out; `collisionHits` counts each collision seen. */\nexport function serviceNameSites(\n file: FileView,\n facts: Pick<UnusedServiceFacts, 'services' | 'collisions'>,\n collisionHits?: Map<string, number>,\n): Array<{ line: number; detail: string }> {\n const out: Array<{ line: number; detail: string }> = [];\n for (const { name } of facts.services) {\n const segment = new RegExp(`^${escapeRegExp(name)}(?:\\\\.[cm]?[jt]sx?)?$`, 'i');\n if (file.path.split('/').some((s) => segment.test(s))) out.push({ line: 1, detail: `the file path is named for ${name}` });\n }\n readableLines(file).forEach((line, index) => {\n const covered: Array<[number, number]> = [];\n for (const { text } of facts.collisions) {\n for (let i = line.indexOf(text); i !== -1; i = line.indexOf(text, i + 1)) {\n covered.push([i, i + text.length]);\n collisionHits?.set(text, (collisionHits.get(text) ?? 0) + 1);\n }\n }\n for (const { name } of facts.services) {\n for (const re of serviceNamePatterns(name)) {\n for (let m = re.exec(line); m; m = re.exec(line)) {\n const start = m.index;\n const end = start + m[0].length;\n if (!covered.some(([a, b]) => a <= start && end <= b)) out.push({ line: index + 1, detail: `names ${name}: ${line.trim().slice(0, 100)}` });\n }\n }\n }\n });\n const seen = new Set<string>();\n return out.filter((s) => {\n const key = `${s.line}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n\nexport const unusedServiceGuard: GenericGuardDefinition<UnusedServiceFacts> = {\n id: 'unusedService',\n absentWhen: 'the app has no external service it must keep out of the product',\n factProblems(facts) {\n const problems: string[] = [];\n reviewedProblems(facts?.services, 'services', problems, { key: 'name', nonEmpty: true });\n for (const s of facts?.services ?? []) if (typeof s?.name === 'string' && !/^[A-Za-z][A-Za-z0-9]*$/.test(s.name)) problems.push(`service ${s.name} is not one word`);\n reviewedProblems(facts?.collisions, 'collisions', problems, { key: 'text' });\n namesProblem(facts?.namedFiles, 'namedFiles', problems, { nonEmpty: false });\n return problems;\n },\n build(facts) {\n const names = facts.services.map((s) => s.name).join(', ');\n return [\n {\n title: 'a service the product does not use is named nowhere',\n description:\n `The product has no surface for ${names}, so nothing names it: no source, test, or script — not its SDK ` +\n 'packages, its client or loader, a module of the tree named for it, a field, callable, hook, or handler built on ' +\n 'its name, or its credentials — and no manifest, deployed configuration, workflow, rules file, or index file. The ' +\n 'service returns only as a product decision that brings its own adapter, secret binding, rules, and tests. Text ' +\n 'that merely contains the name is a reviewed collision with its why; a collision found nowhere is stale.',\n run(context) {\n const findings: Finding[] = [];\n const collisionHits = new Map<string, number>();\n const files = context.files(SERVICE_SCOPE);\n const read = new Set(files.map((f) => f.path));\n for (const path of facts.namedFiles) {\n if (read.has(path)) continue;\n if (!context.reader.exists(path)) findings.push({ file: path, detail: 'a named file no longer exists' });\n else files.push(context.file(path));\n }\n for (const file of files) {\n for (const site of serviceNameSites(file, facts, collisionHits)) findings.push({ file: file.path, line: site.line, detail: site.detail });\n }\n for (const { text } of facts.collisions) if (!collisionHits.get(text)) findings.push({ file: '(collisions)', detail: `stale collision ${text}: it appears nowhere` });\n return findings;\n },\n },\n ];\n },\n};\n"]}
1
+ {"version":3,"file":"monitoring.js","sourceRoot":"","sources":["../../../src/modules/backend/monitoring.ts"],"names":[],"mappings":"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","sourcesContent":["// The monitoring guards: every Cloud Function entry point reports its failures, monitoring is bound\n// once per runtime in its adapters, the provider's SDK is named only where it starts, tests capture\n// through the recording seam, telemetry tags survive the content policy, and a service the app does\n// not use is named nowhere.\n\nimport ts from 'typescript';\nimport type { GenericGuardDefinition } from '../../config.js';\nimport type { Finding, Guard } from '../../guard.js';\nimport type { Scope } from '../../reader/reader.js';\nimport type { FileView } from '../../reader/view.js';\nimport type { Reviewed } from '../../reader/layout.js';\nimport { calleeName, forEachNode, lineOfNode } from '../../support/ast.js';\nimport { loaderProblem, namesProblem, reviewedProblems, textProblem } from '../../support/facts.js';\nimport { MODULE_LOADS, moduleImports, namesModule } from '../../support/imports.js';\nimport { resolveRelative } from '../../support/paths.js';\nimport { escapeRegExp } from '../../support/text.js';\n\nconst FUNCTIONS_CODE: Scope = { roots: ['functions'], kinds: ['code'], tests: false };\nconst APP_CODE: Scope = { roots: ['app'], kinds: ['code'], tests: false };\nconst APP_AND_FUNCTIONS_CODE: Scope = { roots: ['app', 'functions'], kinds: ['code'], tests: false };\n\n/** A pattern for a call to `name` (`a.b` matches `a . b (`), never a longer name ending in it. */\nfunction callPattern(name: string): RegExp {\n return new RegExp(`(?<![\\\\w$])${name.split('.').map(escapeRegExp).join('\\\\s*\\\\.\\\\s*')}\\\\s*\\\\(`, 'g');\n}\n\n/** The index of every match of a pattern in a file's comment-stripped code. */\nfunction matchIndexes(file: FileView, pattern: RegExp): number[] {\n const re = new RegExp(pattern.source, pattern.flags.includes('g') ? pattern.flags : `${pattern.flags}g`);\n const out: number[] = [];\n for (let m = re.exec(file.code); m; m = re.exec(file.code)) out.push(m.index);\n return out;\n}\n\n// ---------------------------------------------------------------------------------------------\n// Monitoring: entry points, one init per runtime, the provider SDK, the recording seam.\n\nexport interface MonitoringFacts {\n /** The one monitoring package every capture goes through. */\n monitoringPackage: string;\n /** The package's init call, which binds the provider. */\n initCall: string;\n /** The package's capture calls. */\n captureCalls: readonly string[];\n /** App calls through which a callable exits, each capturing every fault. */\n callableExits: readonly string[];\n /** The one Functions module that initializes monitoring. */\n functionsInit: string;\n /** The app root's reporter bindings: the file that initializes, and the layout that mounts it as an element. */\n appReporters: ReadonlyArray<{ file: string; mountedIn: string; element: string }>;\n /** The provider SDK's package names (or scope, `@vendor`), named only where the SDK starts. */\n providerSdks: readonly string[];\n /** The provider SDK's own init call (`Vendor.init`). */\n providerInitCall: string;\n /** The files that start the provider SDK, each with its why; one that no longer starts it is stale. */\n sdkInitFiles: readonly Reviewed[];\n /** The Functions test seam that records captures; tests use it instead of mocking the package. */\n functionsTestRecorder: string;\n /**\n * Entry points whose failures their core reports: the entry file, the core module it hands every\n * event to, and the why (`[]` when there is none). An entry is stale when the core no longer\n * captures, the entry file no longer imports it, or the entry file reports on its own.\n */\n reportedByCore: ReadonlyArray<{ path: string; core: string; why: string }>;\n}\n\n/** A module path without its source or emitted extension, for comparing an import to a file. */\nconst withoutExtension = (path: string): string => path.replace(/\\.(?:[cm]?[jt]sx?)$/, '');\n\n/** True when a file imports the module at `corePath` for its values. */\nfunction importsModule(file: FileView, corePath: string): boolean {\n const target = withoutExtension(corePath);\n return file.imports.some(({ spec, typeOnly }) => {\n if (typeOnly) return false;\n const resolved = resolveRelative(file.path, spec);\n return resolved !== null && !resolved.escapes && withoutExtension(resolved.path) === target;\n });\n}\n\n/** True when a file imports the monitoring package and calls one of its capture calls. */\nfunction reportsToMonitoring(file: FileView, facts: Pick<MonitoringFacts, 'monitoringPackage' | 'captureCalls'>): boolean {\n const importsMonitoring = moduleImports(file.sourceFile, MODULE_LOADS).some(({ spec, typeOnly }) => !typeOnly && namesModule(spec, facts.monitoringPackage));\n return importsMonitoring && facts.captureCalls.some((capture) => new RegExp(`\\\\b${escapeRegExp(capture)}\\\\s*\\\\(`).test(file.code));\n}\n\n/** Every Cloud Functions entry-point shape, with the label a finding reports. */\nconst ENTRY_POINT_SHAPES: ReadonlyArray<{ shape: string; re: RegExp }> = [\n { shape: 'onCall', re: /\\bonCall\\s*(?:<[^>]*>)?\\s*\\(/ },\n { shape: 'onRequest', re: /\\bonRequest\\s*\\(/ },\n { shape: 'onSchedule', re: /\\bonSchedule\\s*\\(/ },\n { shape: 'onDocument*', re: /\\bonDocument\\w*\\s*\\(/ },\n { shape: 'onObjectFinalized', re: /\\bonObjectFinalized\\s*\\(/ },\n { shape: 'onObjectDeleted', re: /\\bonObjectDeleted\\s*\\(/ },\n { shape: 'onMessagePublished', re: /\\bonMessagePublished\\s*\\(/ },\n { shape: 'onTaskDispatched', re: /\\bonTaskDispatched\\s*\\(/ },\n { shape: 'onValue*', re: /\\bonValue\\w*\\s*\\(/ },\n];\n\n/** The entry-point shapes a file defines, or none when its failures reach monitoring. */\nexport function unmonitoredEntryPoint(file: FileView, facts: Pick<MonitoringFacts, 'monitoringPackage' | 'captureCalls' | 'callableExits'>): string[] {\n const shapes = ENTRY_POINT_SHAPES.filter(({ re }) => re.test(file.code)).map(({ shape }) => shape);\n if (!shapes.length) return [];\n if (facts.callableExits.some((exit) => callPattern(exit).test(file.code))) return [];\n return reportsToMonitoring(file, facts) ? [] : shapes;\n}\n\n/** Every import of a provider SDK package. */\nexport function providerSdkImports(file: FileView, sdks: readonly string[]): Array<{ node: ts.Node; spec: string }> {\n return moduleImports(file.sourceFile, MODULE_LOADS)\n .filter(({ spec }) => sdks.some((sdk) => namesModule(spec, sdk)))\n .map(({ node, spec }) => ({ node, spec }));\n}\n\n/** Every `vi.mock` / `vi.doMock` of a package. */\nexport function packageMocks(file: FileView, pkg: string): ts.Node[] {\n return moduleImports(file.sourceFile, ['mock'])\n .filter(({ callee, spec }) => (callee === 'vi.mock' || callee === 'vi.doMock') && namesModule(spec, pkg))\n .map(({ node }) => node);\n}\n\n/** True when a file renders `<Element />` (or `<Element>…`). */\nconst mountsElement = (file: FileView, element: string) => new RegExp(`<${escapeRegExp(element)}(?=[\\\\s/>])`).test(file.code);\n\nexport const monitoringGuard: GenericGuardDefinition<MonitoringFacts> = {\n id: 'monitoring',\n absentWhen: false,\n factProblems(facts) {\n const problems: string[] = [];\n textProblem(facts?.monitoringPackage, 'monitoringPackage', problems);\n textProblem(facts?.initCall, 'initCall', problems);\n namesProblem(facts?.captureCalls, 'captureCalls', problems);\n namesProblem(facts?.callableExits, 'callableExits', problems, { nonEmpty: false });\n textProblem(facts?.functionsInit, 'functionsInit', problems);\n if (!Array.isArray(facts?.appReporters)) problems.push('appReporters is missing (declare [] when the app root binds no reporter)');\n for (const r of facts?.appReporters ?? []) {\n if (!r?.file || !r.mountedIn || !r.element) problems.push('an app reporter needs its file, the layout that mounts it, and the element');\n }\n namesProblem(facts?.providerSdks, 'providerSdks', problems);\n textProblem(facts?.providerInitCall, 'providerInitCall', problems);\n reviewedProblems(facts?.sdkInitFiles, 'sdkInitFiles', problems);\n textProblem(facts?.functionsTestRecorder, 'functionsTestRecorder', problems);\n if (!Array.isArray(facts?.reportedByCore)) problems.push('reportedByCore is missing (declare [] when every entry point reports its own failures)');\n for (const entry of facts?.reportedByCore ?? []) {\n if (!entry?.path || !entry.core || !entry.why?.trim()) problems.push('a reportedByCore entry needs its entry file, its core module, and its why');\n }\n return problems;\n },\n build(facts) {\n const initPattern = callPattern(facts.initCall);\n const providerInit = callPattern(facts.providerInitCall);\n const entryPoints: Guard = {\n title: 'every Cloud Function entry point reports its failures to monitoring',\n description:\n 'Every file that defines a deployed Cloud Function — callable, HTTP, schedule, Firestore, Storage, pub/sub, task, ' +\n `or database trigger — either exits its callable through ${facts.callableExits.join(' / ') || 'the app\\'s callable exit'} ` +\n `or imports ${facts.monitoringPackage} and calls ${facts.captureCalls.join(' / ')} on its failure path. A failure ` +\n 'path that only writes a log line is not monitored: nothing watches the log, so a background trigger fails ' +\n 'invisibly. An import that is never called is not monitoring either. An entry point whose core reports every ' +\n 'failure is a reviewed entry naming that core, with its why; it is stale when the core no longer captures, ' +\n 'the entry point no longer imports it, or the entry point reports on its own.',\n run(context) {\n const findings: Finding[] = [];\n const reviewed = new Map(facts.reportedByCore.map((entry) => [entry.path, entry]));\n const seen = new Set<string>();\n for (const file of context.files(FUNCTIONS_CODE)) {\n const shapes = unmonitoredEntryPoint(file, facts);\n const entry = reviewed.get(file.path);\n if (!entry) {\n if (shapes.length) findings.push({ file: file.path, detail: `defines ${shapes.join(', ')} and reports no failure to monitoring` });\n continue;\n }\n seen.add(file.path);\n if (!ENTRY_POINT_SHAPES.some(({ re }) => re.test(file.code))) {\n findings.push({ file: file.path, detail: 'a reportedByCore entry for a file that defines no entry point' });\n } else if (!shapes.length) {\n findings.push({ file: file.path, detail: 'a reportedByCore entry for an entry point that reports on its own' });\n } else if (!importsModule(file, entry.core)) {\n findings.push({ file: file.path, detail: `a reportedByCore entry names ${entry.core}, which the entry point does not import` });\n } else if (!context.reader.exists(entry.core) || !reportsToMonitoring(context.file(entry.core), facts)) {\n findings.push({ file: file.path, detail: `a reportedByCore entry names ${entry.core}, which reports no failure to monitoring` });\n }\n }\n for (const entry of facts.reportedByCore) {\n if (!seen.has(entry.path)) findings.push({ file: entry.path, detail: 'a reportedByCore entry for a file the Functions code does not hold' });\n }\n return findings;\n },\n };\n const initOnce: Guard = {\n title: 'monitoring is initialized once per runtime, only in its adapters',\n description:\n `The Functions runtime initializes monitoring in ${facts.functionsInit} and nowhere else; the app root binds its ` +\n `reporter only in its declared adapters, each initializing once and mounted by its layout. A second ` +\n `${facts.initCall} is a second configuration nothing keeps aligned with the first and double-reports the moment ` +\n 'both run; a missing one leaves every capture writing into an unbound client. Pass the values at the existing ' +\n 'adapter instead of opening a new init.',\n run(context) {\n const findings: Finding[] = [];\n let functionsInits = 0;\n for (const file of context.files(FUNCTIONS_CODE)) {\n const sites = matchIndexes(file, initPattern);\n if (file.path === facts.functionsInit) functionsInits += sites.length;\n else for (const i of sites) findings.push({ file: file.path, line: file.lineOf(i), detail: `${facts.initCall} outside the Functions adapter` });\n }\n if (functionsInits !== 1) findings.push({ file: facts.functionsInit, detail: `the Functions adapter calls ${facts.initCall} ${functionsInits} times, not once` });\n const reporters = new Map(facts.appReporters.map((r) => [r.file, r]));\n const reporterInits = new Map<string, number>();\n for (const file of context.files(APP_CODE)) {\n const sites = matchIndexes(file, initPattern);\n if (reporters.has(file.path)) reporterInits.set(file.path, sites.length);\n else for (const i of sites) findings.push({ file: file.path, line: file.lineOf(i), detail: `${facts.initCall} outside the app's reporter adapters` });\n }\n for (const reporter of facts.appReporters) {\n const count = reporterInits.get(reporter.file) ?? 0;\n if (count !== 1) findings.push({ file: reporter.file, detail: `the reporter adapter calls ${facts.initCall} ${count} times, not once` });\n if (!context.reader.exists(reporter.mountedIn)) findings.push({ file: reporter.mountedIn, detail: 'the layout that mounts the reporter no longer exists' });\n else if (!mountsElement(context.file(reporter.mountedIn), reporter.element)) {\n findings.push({ file: reporter.mountedIn, detail: `does not mount <${reporter.element} />, so the reporter is never bound` });\n }\n }\n return findings;\n },\n };\n const providerNamed: Guard = {\n title: 'the monitoring provider SDK is named only where it starts',\n description:\n `No file in the app or Functions roots imports a provider SDK package (${facts.providerSdks.join(', ')}) or calls ` +\n `${facts.providerInitCall}: the provider binds inside ${facts.monitoringPackage}, and a direct import is a third ` +\n 'binding site whose DSN, environment, and sample rate the app boundary can no longer supply. Capture through the ' +\n 'monitoring package instead. Each declared SDK init file still starts the SDK; one that no longer does is stale.',\n run(context) {\n const findings: Finding[] = [];\n for (const file of context.files(APP_AND_FUNCTIONS_CODE)) {\n for (const { node, spec } of providerSdkImports(file, facts.providerSdks)) {\n findings.push({ file: file.path, line: lineOfNode(file, node), detail: `imports ${spec}` });\n }\n for (const i of matchIndexes(file, providerInit)) findings.push({ file: file.path, line: file.lineOf(i), detail: `calls ${facts.providerInitCall}` });\n }\n for (const entry of facts.sdkInitFiles) {\n if (!context.reader.exists(entry.path)) findings.push({ file: entry.path, detail: 'stale SDK init file: it no longer exists' });\n else if (!matchIndexes(context.file(entry.path), providerInit).length) {\n findings.push({ file: entry.path, detail: `stale SDK init file: it no longer calls ${facts.providerInitCall}` });\n }\n }\n return findings;\n },\n };\n const recorderSeam: Guard = {\n title: 'Functions tests capture through the recording seam, never a local mock of the monitoring package',\n description:\n `No Functions test replaces ${facts.monitoringPackage} with a local vi.mock / vi.doMock. A test that asserts a ` +\n `capture installs the recording adapter (${facts.functionsTestRecorder}) through the package's own adapter seam; a ` +\n 'test that only needs the handler to run needs no mock at all. A local mock lists only the exports its author ' +\n 'knew, and a stubbed scope wrapper never runs the wrapped handler.',\n run(context) {\n const findings: Finding[] = [];\n let seam = false;\n for (const file of context.files({ roots: ['functions'], kinds: ['code'], tests: 'only' })) {\n if (file.path === facts.functionsTestRecorder) seam = true;\n for (const node of packageMocks(file, facts.monitoringPackage)) {\n findings.push({ file: file.path, line: lineOfNode(file, node), detail: `mocks ${facts.monitoringPackage}` });\n }\n }\n if (!seam) findings.push({ file: facts.functionsTestRecorder, detail: 'the recording seam is gone or is not test code' });\n return findings;\n },\n };\n return [entryPoints, initOnce, providerNamed, recorderSeam];\n },\n};\n\n// ---------------------------------------------------------------------------------------------\n// Telemetry tag keys survive the content policy.\n\nexport interface TelemetryTagFacts {\n /** The app's telemetry content policy, as its monitoring package builds it, loaded at run time. */\n policy: () => Promise<{ isDiagnosticKey(key: string): boolean }> | { isDiagnosticKey(key: string): boolean };\n /** Tag keys whose value is always a flag, which passes under any key; a key set nowhere is stale. */\n flagKeys: ReadonlyArray<{ key: string; why: string }>;\n /** The name suffix of a constant whose values are tag keys (`_TAG_KEYS`). */\n tagKeyMapSuffix: string;\n}\n\n/** Every tag key a file sets: a `tags: { … }` literal's keys, `setTag('key', …)`, and a tag-key map's values. */\nexport function tagKeysSet(file: FileView, mapSuffix: string): Array<{ key: string; node: ts.Node }> {\n const sf = file.sourceFile;\n const out: Array<{ key: string; node: ts.Node }> = [];\n const keyOf = (name: ts.PropertyName) => (ts.isIdentifier(name) || ts.isStringLiteralLike(name) ? name.text : undefined);\n forEachNode(sf, (node) => {\n if (ts.isPropertyAssignment(node) && keyOf(node.name) === 'tags' && ts.isObjectLiteralExpression(node.initializer)) {\n for (const prop of node.initializer.properties) {\n const key = (ts.isPropertyAssignment(prop) || ts.isShorthandPropertyAssignment(prop)) && keyOf(prop.name);\n if (key) out.push({ key, node: prop });\n }\n }\n if (ts.isCallExpression(node) && calleeName(node) === 'setTag' && node.arguments[0] && ts.isStringLiteralLike(node.arguments[0])) {\n out.push({ key: node.arguments[0].text, node });\n }\n if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.name.text.endsWith(mapSuffix) && node.initializer) {\n let init: ts.Expression = node.initializer;\n while (ts.isAsExpression(init) || ts.isSatisfiesExpression(init)) init = init.expression;\n if (ts.isObjectLiteralExpression(init)) {\n for (const prop of init.properties) {\n if (ts.isPropertyAssignment(prop) && ts.isStringLiteralLike(prop.initializer)) out.push({ key: prop.initializer.text, node: prop });\n }\n }\n }\n });\n return out;\n}\n\nexport const telemetryTagGuard: GenericGuardDefinition<TelemetryTagFacts> = {\n id: 'telemetryTagKeys',\n absentWhen: 'the app declares no telemetry content policy',\n factProblems(facts) {\n const problems: string[] = [];\n loaderProblem(facts?.policy, 'policy', problems);\n reviewedProblems(facts?.flagKeys, 'flagKeys', problems, { key: 'key' });\n textProblem(facts?.tagKeyMapSuffix, 'tagKeyMapSuffix', problems);\n return problems;\n },\n build(facts) {\n return [\n {\n title: 'every backend telemetry tag key survives the content policy',\n description:\n 'A tag the backend sets survives the telemetry content policy only under a diagnostic key, so every tag key a ' +\n \"capture site sets — a `tags: { … }` literal's keys, a `setTag('key', …)`, a tag-key map's values — is one; \" +\n 'otherwise the policy silently drops it. Rename the tag to a diagnostic key, or widen the policy only to a key ' +\n 'that never holds prose. A key whose value is always a flag is a reviewed entry with its why.',\n async run(context) {\n const policy = await facts.policy();\n const flags = new Set(facts.flagKeys.map((f) => f.key));\n const findings: Finding[] = [];\n const seen = new Set<string>();\n for (const file of context.files(FUNCTIONS_CODE)) {\n for (const { key, node } of tagKeysSet(file, facts.tagKeyMapSuffix)) {\n seen.add(key);\n if (!flags.has(key) && !policy.isDiagnosticKey(key)) {\n findings.push({ file: file.path, line: lineOfNode(file, node), detail: `tag key ${key} is not a diagnostic key, so the policy drops it` });\n }\n }\n }\n if (!seen.size) findings.push({ file: '(functions)', detail: 'no tag key found, so the guard checks nothing' });\n for (const key of flags) if (!seen.has(key)) findings.push({ file: '(flagKeys)', detail: `stale flag key ${key}: no capture site sets it` });\n return findings;\n },\n },\n ];\n },\n};\n\n// ---------------------------------------------------------------------------------------------\n// A service the app does not use is named nowhere.\n\nexport interface UnusedServiceFacts {\n /** Services the product has no surface for, each with its why. */\n services: ReadonlyArray<{ name: string; why: string }>;\n /** Text that contains a service's name but is not the service (a word it is part of), each with its why. */\n collisions: ReadonlyArray<{ text: string; why: string }>;\n /** Files outside the roots read by name: manifests, deployed configuration, workflows, rules, index files. */\n namedFiles: readonly string[];\n}\n\nconst SERVICE_SCOPE: Scope = {\n roots: ['app', 'functions', 'e2e', 'hostedE2e', 'scripts', 'workers'],\n optionalRoots: ['functions', 'e2e', 'hostedE2e', 'scripts', 'workers'],\n kinds: ['code', 'config', 'rules'],\n tests: true,\n};\n\n/**\n * Where a service's name appears as a name: its lowercase form starting a word and not running on\n * into lowercase letters (`svc`, `svcSessionId`, `svc.`, `'svc'`, `@svc/`); its capitalized form\n * anywhere not running on (`Svc`, `loadSvc`); its uppercase form standing alone between non-letters\n * (`SVC_KEY`). A longer word that merely contains it (`svced`, `pinsvc`) is not the service.\n */\nexport function serviceNamePatterns(name: string): RegExp[] {\n const lower = escapeRegExp(name.toLowerCase());\n const cap = escapeRegExp(name.charAt(0).toUpperCase() + name.slice(1).toLowerCase());\n const upper = escapeRegExp(name.toUpperCase());\n return [\n new RegExp(`(?<![A-Za-z0-9])${lower}(?![a-z])`, 'g'),\n new RegExp(`${cap}(?![a-z])`, 'g'),\n new RegExp(`(?<![A-Z0-9])${upper}(?![A-Z0-9])`, 'g'),\n ];\n}\n\n/** The lines of a file's text a reader of the service name reads: comments skipped by line. */\nfunction readableLines(file: FileView): string[] {\n const lines = file.code.split('\\n');\n const name = file.path.slice(file.path.lastIndexOf('/') + 1);\n if (file.kind === 'config' && !/\\.jsonc?$/.test(name)) return lines.map((l) => (/^\\s*#/.test(l) ? '' : l));\n if (file.kind === 'rules') return lines.map((l) => (/^\\s*\\/\\//.test(l) ? '' : l));\n return lines;\n}\n\n/** Every place a file names a service, collisions taken out; `collisionHits` counts each collision seen. */\nexport function serviceNameSites(\n file: FileView,\n facts: Pick<UnusedServiceFacts, 'services' | 'collisions'>,\n collisionHits?: Map<string, number>,\n): Array<{ line: number; detail: string }> {\n const out: Array<{ line: number; detail: string }> = [];\n for (const { name } of facts.services) {\n const segment = new RegExp(`^${escapeRegExp(name)}(?:\\\\.[cm]?[jt]sx?)?$`, 'i');\n if (file.path.split('/').some((s) => segment.test(s))) out.push({ line: 1, detail: `the file path is named for ${name}` });\n }\n readableLines(file).forEach((line, index) => {\n const covered: Array<[number, number]> = [];\n for (const { text } of facts.collisions) {\n for (let i = line.indexOf(text); i !== -1; i = line.indexOf(text, i + 1)) {\n covered.push([i, i + text.length]);\n collisionHits?.set(text, (collisionHits.get(text) ?? 0) + 1);\n }\n }\n for (const { name } of facts.services) {\n for (const re of serviceNamePatterns(name)) {\n for (let m = re.exec(line); m; m = re.exec(line)) {\n const start = m.index;\n const end = start + m[0].length;\n if (!covered.some(([a, b]) => a <= start && end <= b)) out.push({ line: index + 1, detail: `names ${name}: ${line.trim().slice(0, 100)}` });\n }\n }\n }\n });\n const seen = new Set<string>();\n return out.filter((s) => {\n const key = `${s.line}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n\nexport const unusedServiceGuard: GenericGuardDefinition<UnusedServiceFacts> = {\n id: 'unusedService',\n absentWhen: 'the app has no external service it must keep out of the product',\n factProblems(facts) {\n const problems: string[] = [];\n reviewedProblems(facts?.services, 'services', problems, { key: 'name', nonEmpty: true });\n for (const s of facts?.services ?? []) if (typeof s?.name === 'string' && !/^[A-Za-z][A-Za-z0-9]*$/.test(s.name)) problems.push(`service ${s.name} is not one word`);\n reviewedProblems(facts?.collisions, 'collisions', problems, { key: 'text' });\n namesProblem(facts?.namedFiles, 'namedFiles', problems, { nonEmpty: false });\n return problems;\n },\n build(facts) {\n const names = facts.services.map((s) => s.name).join(', ');\n return [\n {\n title: 'a service the product does not use is named nowhere',\n description:\n `The product has no surface for ${names}, so nothing names it: no source, test, or script — not its SDK ` +\n 'packages, its client or loader, a module of the tree named for it, a field, callable, hook, or handler built on ' +\n 'its name, or its credentials — and no manifest, deployed configuration, workflow, rules file, or index file. The ' +\n 'service returns only as a product decision that brings its own adapter, secret binding, rules, and tests. Text ' +\n 'that merely contains the name is a reviewed collision with its why; a collision found nowhere is stale.',\n run(context) {\n const findings: Finding[] = [];\n const collisionHits = new Map<string, number>();\n const files = context.files(SERVICE_SCOPE);\n const read = new Set(files.map((f) => f.path));\n for (const path of facts.namedFiles) {\n if (read.has(path)) continue;\n if (!context.reader.exists(path)) findings.push({ file: path, detail: 'a named file no longer exists' });\n else files.push(context.file(path));\n }\n for (const file of files) {\n for (const site of serviceNameSites(file, facts, collisionHits)) findings.push({ file: file.path, line: site.line, detail: site.detail });\n }\n for (const { text } of facts.collisions) if (!collisionHits.get(text)) findings.push({ file: '(collisions)', detail: `stale collision ${text}: it appears nowhere` });\n return findings;\n },\n },\n ];\n },\n};\n"]}
@@ -27,7 +27,7 @@ export declare function boundaryDerivations(file: FileView, facts: Pick<ClientAu
27
27
  detail: string;
28
28
  }>;
29
29
  /** A second export of the accessor's name, or a context holding auth vocabulary, in one file. */
30
- export declare function secondAuthAccessors(path: string, code: string, facts: Pick<ClientAuthBoundaryFacts, 'accessor' | 'authContextVocabulary' | 'boundaryModules'>): Array<{
30
+ export declare function secondAuthAccessors(path: string, code: string, sourceFile: ts.SourceFile, facts: Pick<ClientAuthBoundaryFacts, 'accessor' | 'authContextVocabulary' | 'boundaryModules'>): Array<{
31
31
  index: number;
32
32
  detail: string;
33
33
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/modules/frontend-actions/auth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/modules/frontend-actions/auth.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  // The guards over the client's auth boundary: one place that answers "who is this", auth failures
2
2
  // that reach monitoring, sign-out liveness through its owner, and editors no permission read can drop.
3
3
  import ts from 'typescript';
4
+ import { calleeName, forEachNode } from '../../support/ast.js';
4
5
  import { namesProblem, objectProblem, reviewedProblems, textProblem } from '../../support/facts.js';
5
6
  import { fromStatements, moduleImports, namesModule } from '../../support/imports.js';
6
7
  import { escapeRegExp, isUnder } from '../../support/text.js';
@@ -45,7 +46,7 @@ export function boundaryDerivations(file, facts) {
45
46
  return out;
46
47
  }
47
48
  /** A second export of the accessor's name, or a context holding auth vocabulary, in one file. */
48
- export function secondAuthAccessors(path, code, facts) {
49
+ export function secondAuthAccessors(path, code, sourceFile, facts) {
49
50
  const out = [];
50
51
  const name = escapeRegExp(facts.accessor.name);
51
52
  if (path !== facts.accessor.file) {
@@ -57,13 +58,56 @@ export function secondAuthAccessors(path, code, facts) {
57
58
  }
58
59
  if (!facts.boundaryModules.some((m) => m.path === path)) {
59
60
  const vocabulary = new RegExp(`\\b(?:${facts.authContextVocabulary.map(escapeRegExp).join('|')})\\b`);
60
- if (vocabulary.test(code)) {
61
- for (const m of code.matchAll(/\bcreateContext\s*[<(]/g))
62
- out.push({ index: m.index, detail: 'a second auth context' });
61
+ for (const call of contextCreations(sourceFile)) {
62
+ if (contextShapeTexts(call, sourceFile, code).some((text) => vocabulary.test(text))) {
63
+ out.push({ index: call.getStart(sourceFile), detail: 'a second auth context' });
64
+ }
63
65
  }
64
66
  }
65
67
  return out;
66
68
  }
69
+ /** Every `createContext(…)` call in a file, bare or through a namespace. */
70
+ function contextCreations(sourceFile) {
71
+ const calls = [];
72
+ forEachNode(sourceFile, (node) => {
73
+ if (ts.isCallExpression(node) && calleeName(node) === 'createContext')
74
+ calls.push(node);
75
+ });
76
+ return calls;
77
+ }
78
+ /**
79
+ * The comment-free text of what a context holds: its type argument, the same-file interface or type
80
+ * alias that argument names, its default value, and the same-file constant that value names. Only
81
+ * these say whether the context carries auth state; the rest of the file does not.
82
+ */
83
+ function contextShapeTexts(call, sourceFile, code) {
84
+ const textOf = (node) => code.slice(node.getStart(sourceFile), node.getEnd());
85
+ const declared = (name) => {
86
+ const found = [];
87
+ forEachNode(sourceFile, (node) => {
88
+ if ((ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node)) && node.name.text === name)
89
+ found.push(node);
90
+ if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.name.text === name && node.initializer)
91
+ found.push(node.initializer);
92
+ });
93
+ return found;
94
+ };
95
+ const texts = [];
96
+ for (const typeArg of call.typeArguments ?? []) {
97
+ texts.push(textOf(typeArg));
98
+ forEachNode(typeArg, (node) => {
99
+ if (ts.isTypeReferenceNode(node) && ts.isIdentifier(node.typeName))
100
+ texts.push(...declared(node.typeName.text).map(textOf));
101
+ });
102
+ }
103
+ const initial = call.arguments[0];
104
+ if (initial) {
105
+ texts.push(textOf(initial));
106
+ if (ts.isIdentifier(initial))
107
+ texts.push(...declared(initial.text).map(textOf));
108
+ }
109
+ return texts;
110
+ }
67
111
  export const clientAuthBoundaryGuard = {
68
112
  id: 'clientAuthBoundary',
69
113
  absentWhen: 'the app has no signed-in users',
@@ -96,7 +140,7 @@ export const clientAuthBoundaryGuard = {
96
140
  const inBoundary = facts.boundaryModules.some((m) => m.path === file.path);
97
141
  const hits = [
98
142
  ...(inBoundary ? [] : boundaryDerivations(file, facts)),
99
- ...secondAuthAccessors(file.path, file.code, facts),
143
+ ...secondAuthAccessors(file.path, file.code, file.sourceFile, facts),
100
144
  ];
101
145
  for (const hit of hits)
102
146
  findings.push({ file: file.path, line: file.lineOf(hit.index), detail: hit.detail });
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/modules/frontend-actions/auth.ts"],"names":[],"mappings":"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","sourcesContent":["// The guards over the client's auth boundary: one place that answers \"who is this\", auth failures\n// that reach monitoring, sign-out liveness through its owner, and editors no permission read can drop.\n\nimport ts from 'typescript';\nimport type { GenericGuardDefinition } from '../../config.js';\nimport type { Finding, Guard } from '../../guard.js';\nimport type { Reviewed } from '../../reader/layout.js';\nimport type { Scope } from '../../reader/reader.js';\nimport type { FileView } from '../../reader/view.js';\nimport { namesProblem, objectProblem, reviewedProblems, textProblem } from '../../support/facts.js';\nimport { fromStatements, moduleImports, namesModule } from '../../support/imports.js';\nimport { escapeRegExp, isUnder } from '../../support/text.js';\nimport { memberOrBareCalls, throughNamespace } from './text.js';\n\nconst APP_CODE: Scope = { roots: ['app'], kinds: ['code'], tests: false };\n\n/** The keywords that declare a name a module can export: a function, a binding, or a class. */\nconst DECLARATION_KEYWORDS = '(?:function|const|let|class)';\nconst declares = (code: string, name: string) => new RegExp(`\\\\b${DECLARATION_KEYWORDS}\\\\s+${escapeRegExp(name)}\\\\b`).test(code);\n\n// ---------------------------------------------------------------------------------------------\n// Client auth boundary: one derivation of identity and claims, and one auth context and accessor.\n// Where a signed-out visitor is sent is business: an app browsable signed out shows a sign-in gate in\n// place, an app that requires sign-in sends the visitor to its sign-in route. It is not checked here.\n\nexport interface ClientAuthBoundaryFacts {\n /** The modules that ARE the boundary — the provider mount, its configuration, the accessor — each with why. */\n boundaryModules: readonly Reviewed[];\n /** The one accessor every other module reads auth through. */\n accessor: { file: string; name: string };\n /** Symbols that derive identity, claims, or auth loading state. */\n derivationSymbols: readonly string[];\n /** Modules those symbols can be reached through as a namespace (a module covers its subpaths). */\n authSourceModules: readonly string[];\n /** Names only an auth context holds; a `createContext` beside one is a second auth context. */\n authContextVocabulary: readonly string[];\n}\n\n/**\n * Each place outside the boundary that derives identity, claims, or auth loading itself. A call is\n * matched bare or as a member (`auth.onAuthStateChanged(cb)`, `getAuth().onIdTokenChanged(cb)`),\n * since the SDK hands out the same derivations as instance methods.\n */\nexport function boundaryDerivations(file: FileView, facts: Pick<ClientAuthBoundaryFacts, 'derivationSymbols' | 'authSourceModules'>): Array<{ index: number; detail: string }> {\n const { code, sourceFile } = file;\n const out: Array<{ index: number; detail: string }> = [];\n for (const site of moduleImports(sourceFile, ['import'])) {\n if (site.typeOnly) continue;\n for (const { imported, typeOnly } of site.named) {\n if (!typeOnly && facts.derivationSymbols.includes(imported)) out.push({ index: site.node.getStart(sourceFile), detail: `imports ${imported}` });\n }\n }\n const namespaces: string[] = [];\n for (const site of fromStatements(sourceFile)) {\n if (site.namespace && facts.authSourceModules.some((m) => namesModule(site.spec, m))) namespaces.push(site.namespace);\n }\n for (const namespace of namespaces) {\n for (const symbol of facts.derivationSymbols) {\n const m = new RegExp(`\\\\b${escapeRegExp(namespace)}\\\\s*\\\\.\\\\s*${escapeRegExp(symbol)}\\\\b`).exec(code);\n if (m) out.push({ index: m.index, detail: `reaches ${namespace}.${symbol}` });\n }\n }\n for (const symbol of facts.derivationSymbols) {\n const call = memberOrBareCalls(code, symbol).find((index) => !throughNamespace(code, index, namespaces));\n if (call !== undefined) out.push({ index: call, detail: `calls ${symbol}(` });\n }\n return out;\n}\n\n/** A second export of the accessor's name, or a context holding auth vocabulary, in one file. */\nexport function secondAuthAccessors(\n path: string,\n code: string,\n facts: Pick<ClientAuthBoundaryFacts, 'accessor' | 'authContextVocabulary' | 'boundaryModules'>,\n): Array<{ index: number; detail: string }> {\n const out: Array<{ index: number; detail: string }> = [];\n const name = escapeRegExp(facts.accessor.name);\n if (path !== facts.accessor.file) {\n const exported =\n new RegExp(`\\\\bexport\\\\s+function\\\\s+${name}\\\\s*[(<]`).exec(code) ??\n new RegExp(`\\\\bexport\\\\s+(?:const|let|var)\\\\s+${name}\\\\b`).exec(code) ??\n new RegExp(`\\\\bexport\\\\s*\\\\{[^}]*\\\\b${name}\\\\b[^}]*\\\\}`).exec(code);\n if (exported) out.push({ index: exported.index, detail: `a second export of ${facts.accessor.name}` });\n }\n if (!facts.boundaryModules.some((m) => m.path === path)) {\n const vocabulary = new RegExp(`\\\\b(?:${facts.authContextVocabulary.map(escapeRegExp).join('|')})\\\\b`);\n if (vocabulary.test(code)) {\n for (const m of code.matchAll(/\\bcreateContext\\s*[<(]/g)) out.push({ index: m.index!, detail: 'a second auth context' });\n }\n }\n return out;\n}\n\nexport const clientAuthBoundaryGuard: GenericGuardDefinition<ClientAuthBoundaryFacts> = {\n id: 'clientAuthBoundary',\n absentWhen: 'the app has no signed-in users',\n factProblems(facts) {\n const problems: string[] = [];\n reviewedProblems(facts?.boundaryModules, 'boundaryModules', problems);\n if (objectProblem(facts?.accessor, 'accessor', problems)) {\n textProblem(facts.accessor.file, 'accessor.file', problems);\n textProblem(facts.accessor.name, 'accessor.name', problems);\n if (Array.isArray(facts.boundaryModules) && !facts.boundaryModules.some((m) => m?.path === facts.accessor.file)) {\n problems.push(`the accessor ${facts.accessor.file} is not one of the boundaryModules`);\n }\n }\n namesProblem(facts?.derivationSymbols, 'derivationSymbols', problems);\n namesProblem(facts?.authSourceModules, 'authSourceModules', problems);\n namesProblem(facts?.authContextVocabulary, 'authContextVocabulary', problems);\n return problems;\n },\n build(facts) {\n return [\n {\n title: 'the client has one auth boundary',\n description:\n `Identity, claims, and auth loading state come from one provider, read through \\`${facts.accessor.name}\\` ` +\n `(${facts.accessor.file}). Outside the boundary modules nothing imports, reaches, or calls ` +\n `${facts.derivationSymbols.join(', ')}; no other module exports \\`${facts.accessor.name}\\` or creates a ` +\n 'context holding auth state. Read the accessor.',\n run(context) {\n const findings: Finding[] = [];\n for (const file of context.files(APP_CODE)) {\n const inBoundary = facts.boundaryModules.some((m) => m.path === file.path);\n const hits = [\n ...(inBoundary ? [] : boundaryDerivations(file, facts)),\n ...secondAuthAccessors(file.path, file.code, facts),\n ];\n for (const hit of hits) findings.push({ file: file.path, line: file.lineOf(hit.index), detail: hit.detail });\n }\n for (const module of facts.boundaryModules) {\n if (!context.reader.exists(module.path)) findings.push({ file: module.path, detail: 'stale boundary module: it no longer exists' });\n }\n if (context.reader.exists(facts.accessor.file)) {\n const code = context.file(facts.accessor.file).code;\n if (!new RegExp(`\\\\bexport\\\\s+(?:function|const)\\\\s+${escapeRegExp(facts.accessor.name)}\\\\b`).test(code)) {\n findings.push({ file: facts.accessor.file, detail: `the accessor no longer exports ${facts.accessor.name}` });\n }\n }\n return findings;\n },\n },\n ];\n },\n};\n\n// ---------------------------------------------------------------------------------------------\n// Banned auth calls: a call the auth surfaces must never make.\n\nexport interface BannedAuthCallFacts {\n /** The folders and files that make up the auth surfaces. */\n surfaces: readonly string[];\n /** Each banned call and why it is banned. */\n banned: ReadonlyArray<{ name: string; why: string }>;\n /** A long-lived auth surface the scan must reach. */\n anchor: string;\n}\n\nexport const bannedAuthCallGuard: GenericGuardDefinition<BannedAuthCallFacts> = {\n id: 'bannedAuthCalls',\n absentWhen: 'the app has no signed-in users',\n factProblems(facts) {\n const problems: string[] = [];\n namesProblem(facts?.surfaces, 'surfaces', problems);\n if (!Array.isArray(facts?.banned) || !facts.banned.length) problems.push('banned is missing or empty');\n else for (const b of facts.banned) {\n textProblem(b?.name, 'a banned call', problems);\n textProblem(b?.why, `banned call ${String(b?.name)}'s why`, problems);\n }\n textProblem(facts?.anchor, 'anchor', problems);\n if (typeof facts?.anchor === 'string' && Array.isArray(facts?.surfaces) && !facts.surfaces.some((s) => isUnder(facts.anchor, s))) {\n problems.push(`the anchor ${facts.anchor} is not inside any surface`);\n }\n return problems;\n },\n build(facts) {\n return [\n {\n title: 'the auth surfaces never name a banned auth call',\n description:\n `No file under ${facts.surfaces.join(', ')} imports or calls ` +\n facts.banned.map((b) => `\\`${b.name}\\` (${b.why})`).join('; ') +\n '. The symbol must not be reachable from these modules at all.',\n run(context) {\n const findings: Finding[] = [];\n const files = context.files(APP_CODE).filter((f) => facts.surfaces.some((s) => isUnder(f.path, s)));\n if (!files.some((f) => f.path === facts.anchor)) findings.push({ file: facts.anchor, detail: 'the anchor surface was not reached (gone, or outside the surfaces)' });\n for (const file of files) {\n for (const { name, why } of facts.banned) {\n const re = new RegExp(`\\\\b${escapeRegExp(name)}\\\\b`, 'g');\n for (let m = re.exec(file.code); m; m = re.exec(file.code)) {\n findings.push({ file: file.path, line: file.lineOf(m.index), detail: `names ${name}: ${why}` });\n }\n }\n }\n return findings;\n },\n },\n ];\n },\n};\n\n// ---------------------------------------------------------------------------------------------\n// Auth failure capture: every source calling a Firebase Auth operation that can fail reports it.\n\nexport interface AuthFailureCaptureFacts {\n /** The function every auth failure is reported through. */\n reporter: string;\n /** Sources that leave the report to the code that catches their failure, each with why and the file that reports it. */\n reportedElsewhere: ReadonlyArray<{ path: string; why: string; reportedBy: string }>;\n}\n\n/** The Firebase Auth operations that reach the service and can fail. */\nconst FAILING_AUTH_OPERATION =\n /^(?:createUserWithEmailAndPassword|signInWith\\w+|signOut|sendPasswordResetEmail|confirmPasswordReset|verifyPasswordResetCode|checkActionCode|applyActionCode|sendEmailVerification|reauthenticateWith\\w+|updatePassword|updateEmail|verifyBeforeUpdateEmail|linkWith\\w+|deleteUser)$/;\n\n/** The failing operations a source imports as values from the Firebase Auth SDK. */\nexport function failingAuthImports(file: FileView): string[] {\n const names: string[] = [];\n for (const site of moduleImports(file.sourceFile, ['import'])) {\n if (site.typeOnly || site.spec !== 'firebase/auth') continue;\n for (const { imported, typeOnly } of site.named) if (!typeOnly && FAILING_AUTH_OPERATION.test(imported)) names.push(imported);\n }\n return names;\n}\n\nexport const authFailureCaptureGuard: GenericGuardDefinition<AuthFailureCaptureFacts> = {\n id: 'authFailureCapture',\n absentWhen: 'the app has no signed-in users',\n factProblems(facts) {\n const problems: string[] = [];\n textProblem(facts?.reporter, 'reporter', problems);\n reviewedProblems(facts?.reportedElsewhere, 'reportedElsewhere', problems);\n for (const e of Array.isArray(facts?.reportedElsewhere) ? facts.reportedElsewhere : []) {\n if (e && (typeof e.reportedBy !== 'string' || !e.reportedBy.trim())) {\n problems.push(`reportedElsewhere entry ${e.path} names no reportedBy file (the file that reports its failures)`);\n }\n }\n return problems;\n },\n build(facts) {\n const reports = new RegExp(`\\\\b${escapeRegExp(facts.reporter)}\\\\s*\\\\(`);\n return [\n {\n title: 'every Firebase Auth failure reaches monitoring',\n description:\n `A source that calls a Firebase Auth operation that can fail reports its failure through \\`${facts.reporter}\\`; ` +\n 'a catch that only shows a message leaves a broken sign-up or sign-in invisible to monitoring. A source that ' +\n 'never catches the failure itself is a reviewed entry naming who reports for it; an entry that no longer calls ' +\n 'a failing operation, now reports itself, or whose reporter no longer reports is stale.',\n run(context) {\n const findings: Finding[] = [];\n const callers = context.files(APP_CODE).map((f) => ({ file: f, ops: failingAuthImports(f) })).filter((c) => c.ops.length);\n if (!callers.length) findings.push({ file: '(app)', detail: 'no source imports a failing Firebase Auth operation — the scan found no auth surface' });\n const reviewed = new Map(facts.reportedElsewhere.map((e) => [e.path, e]));\n for (const { file, ops } of callers) {\n if (!reports.test(file.code) && !reviewed.has(file.path)) findings.push({ file: file.path, detail: `calls ${ops.join(', ')} and never reports a failure` });\n }\n for (const entry of facts.reportedElsewhere) {\n const caller = callers.find((c) => c.file.path === entry.path);\n if (!caller) findings.push({ file: entry.path, detail: 'stale entry: it no longer calls a failing auth operation' });\n else if (reports.test(caller.file.code)) findings.push({ file: entry.path, detail: 'stale entry: it reports its own failures now' });\n if (!context.reader.exists(entry.reportedBy)) findings.push({ file: entry.reportedBy, detail: `the reporter named for ${entry.path} no longer exists` });\n else if (!reports.test(context.file(entry.reportedBy).code)) findings.push({ file: entry.reportedBy, detail: `the reporter named for ${entry.path} no longer reports` });\n }\n return findings;\n },\n },\n ];\n },\n};\n\n// ---------------------------------------------------------------------------------------------\n// Sign-out liveness: the \"still signed in\" check has one owner.\n\nexport interface SignOutLivenessFacts {\n /** The module that owns the liveness check. */\n owner: string;\n /** The names the owner exports for it. */\n ownerExports: readonly string[];\n /** The function that returns the client auth instance. */\n authAccessor: string;\n}\n\nexport const signOutLivenessGuard: GenericGuardDefinition<SignOutLivenessFacts> = {\n id: 'signOutLiveness',\n absentWhen: 'the app has no signed-in users',\n factProblems(facts) {\n const problems: string[] = [];\n textProblem(facts?.owner, 'owner', problems);\n namesProblem(facts?.ownerExports, 'ownerExports', problems);\n textProblem(facts?.authAccessor, 'authAccessor', problems);\n return problems;\n },\n build(facts) {\n const redeclared = new RegExp(`\\\\b${DECLARATION_KEYWORDS}\\\\s+(?:${facts.ownerExports.map(escapeRegExp).join('|')})\\\\b`, 'g');\n const negatedSkip = new RegExp(`!\\\\s*${escapeRegExp(facts.authAccessor)}\\\\(\\\\)\\\\s*\\\\??\\\\.\\\\s*currentUser`, 'g');\n return [\n {\n title: 'the signed-in check before an awaited call has one owner',\n description:\n `A check that the user is still signed in, made across an awaited hop before a call, is ${facts.owner}'s ` +\n `(${facts.ownerExports.join(', ')}). Nothing else re-declares those names or hand-rolls a negated ` +\n `\\`${facts.authAccessor}().currentUser\\` skip guard: import the owner.`,\n run(context) {\n const findings: Finding[] = [];\n for (const file of context.files(APP_CODE)) {\n if (file.path === facts.owner) continue;\n for (const re of [redeclared, negatedSkip]) {\n re.lastIndex = 0;\n for (let m = re.exec(file.code); m; m = re.exec(file.code)) {\n findings.push({ file: file.path, line: file.lineOf(m.index), detail: re === redeclared ? `re-declares ${m[0].split(/\\s+/).pop()}` : 'hand-rolls a negated currentUser skip guard' });\n }\n }\n }\n if (!context.reader.exists(facts.owner)) findings.push({ file: facts.owner, detail: 'the owner no longer exists' });\n else {\n const code = context.file(facts.owner).code;\n for (const name of facts.ownerExports) if (!declares(code, name)) findings.push({ file: facts.owner, detail: `the owner no longer declares ${name}` });\n }\n return findings;\n },\n },\n ];\n },\n};\n\n// ---------------------------------------------------------------------------------------------\n// Editor permission gate: an editor holding typed input is never mounted under a permission read.\n\nexport interface EditorPermissionGateFacts {\n /** The hook an editor calls to guard its unsaved input: a file calling it holds an editor. */\n editorMarker: string;\n /** The element the editor mounts in. */\n editorElement: string;\n /** Permission reads: identifiers starting with this prefix and a capital letter. */\n permissionNamePrefix: string;\n /** Permission reads: calls to these functions (declare [] when none). */\n permissionCalls: readonly string[];\n}\n\n/** Each editor element with an ancestor `{<permission read> && …}`. */\nexport function permissionGatedEditors(sf: ts.SourceFile, facts: Omit<EditorPermissionGateFacts, 'editorMarker'>): ts.Node[] {\n const prefix = new RegExp(`^${escapeRegExp(facts.permissionNamePrefix)}[A-Z]`);\n const readsPermission = (expr: ts.Expression) => {\n let found = false;\n const visit = (node: ts.Node) => {\n if (ts.isIdentifier(node) && prefix.test(node.text)) found = true;\n if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && facts.permissionCalls.includes(node.expression.text)) found = true;\n ts.forEachChild(node, visit);\n };\n visit(expr);\n return found;\n };\n const out: ts.Node[] = [];\n const visit = (node: ts.Node) => {\n if ((ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) && node.tagName.getText(sf) === facts.editorElement) {\n for (let p: ts.Node | undefined = node.parent; p; p = p.parent) {\n if (\n ts.isJsxExpression(p) &&\n p.expression &&\n ts.isBinaryExpression(p.expression) &&\n p.expression.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken &&\n readsPermission(p.expression.left)\n ) {\n out.push(node);\n break;\n }\n }\n }\n ts.forEachChild(node, visit);\n };\n visit(sf);\n return out;\n}\n\nexport const editorPermissionGateGuard: GenericGuardDefinition<EditorPermissionGateFacts> = {\n id: 'editorPermissionGate',\n absentWhen: false,\n factProblems(facts) {\n const problems: string[] = [];\n textProblem(facts?.editorMarker, 'editorMarker', problems);\n textProblem(facts?.editorElement, 'editorElement', problems);\n textProblem(facts?.permissionNamePrefix, 'permissionNamePrefix', problems);\n namesProblem(facts?.permissionCalls, 'permissionCalls', problems, { nonEmpty: false });\n return problems;\n },\n build(facts): Guard[] {\n return [\n {\n title: 'an editor holding typed input is never mounted under a permission gate',\n description:\n `A component that guards its unsaved input (\\`${facts.editorMarker}\\`) gates only the control that opens its ` +\n `editor on a permission; its \\`<${facts.editorElement}>\\` stays mounted, so a permission read that changes or ` +\n 'fails while it is open can never unmount it and drop what was typed. Gate the trigger, and key the editor to ' +\n 'the account that opened it.',\n run(context) {\n const findings: Finding[] = [];\n const marker = new RegExp(`\\\\b${escapeRegExp(facts.editorMarker)}\\\\s*\\\\(`);\n const editors = context.files(APP_CODE).filter((f) => marker.test(f.code));\n if (!editors.length) findings.push({ file: '(app)', detail: `no component calls ${facts.editorMarker} — the scan found no editor to check` });\n for (const file of editors) {\n for (const node of permissionGatedEditors(file.sourceFile, facts)) {\n findings.push({ file: file.path, line: file.lineOf(node.getStart(file.sourceFile)), detail: `<${facts.editorElement}> mounted under a permission gate` });\n }\n }\n return findings;\n },\n },\n ];\n },\n};\n"]}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/modules/frontend-actions/auth.ts"],"names":[],"mappings":"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","sourcesContent":["// The guards over the client's auth boundary: one place that answers \"who is this\", auth failures\n// that reach monitoring, sign-out liveness through its owner, and editors no permission read can drop.\n\nimport ts from 'typescript';\nimport type { GenericGuardDefinition } from '../../config.js';\nimport type { Finding, Guard } from '../../guard.js';\nimport type { Reviewed } from '../../reader/layout.js';\nimport type { Scope } from '../../reader/reader.js';\nimport type { FileView } from '../../reader/view.js';\nimport { calleeName, forEachNode } from '../../support/ast.js';\nimport { namesProblem, objectProblem, reviewedProblems, textProblem } from '../../support/facts.js';\nimport { fromStatements, moduleImports, namesModule } from '../../support/imports.js';\nimport { escapeRegExp, isUnder } from '../../support/text.js';\nimport { memberOrBareCalls, throughNamespace } from './text.js';\n\nconst APP_CODE: Scope = { roots: ['app'], kinds: ['code'], tests: false };\n\n/** The keywords that declare a name a module can export: a function, a binding, or a class. */\nconst DECLARATION_KEYWORDS = '(?:function|const|let|class)';\nconst declares = (code: string, name: string) => new RegExp(`\\\\b${DECLARATION_KEYWORDS}\\\\s+${escapeRegExp(name)}\\\\b`).test(code);\n\n// ---------------------------------------------------------------------------------------------\n// Client auth boundary: one derivation of identity and claims, and one auth context and accessor.\n// Where a signed-out visitor is sent is business: an app browsable signed out shows a sign-in gate in\n// place, an app that requires sign-in sends the visitor to its sign-in route. It is not checked here.\n\nexport interface ClientAuthBoundaryFacts {\n /** The modules that ARE the boundary — the provider mount, its configuration, the accessor — each with why. */\n boundaryModules: readonly Reviewed[];\n /** The one accessor every other module reads auth through. */\n accessor: { file: string; name: string };\n /** Symbols that derive identity, claims, or auth loading state. */\n derivationSymbols: readonly string[];\n /** Modules those symbols can be reached through as a namespace (a module covers its subpaths). */\n authSourceModules: readonly string[];\n /** Names only an auth context holds; a `createContext` beside one is a second auth context. */\n authContextVocabulary: readonly string[];\n}\n\n/**\n * Each place outside the boundary that derives identity, claims, or auth loading itself. A call is\n * matched bare or as a member (`auth.onAuthStateChanged(cb)`, `getAuth().onIdTokenChanged(cb)`),\n * since the SDK hands out the same derivations as instance methods.\n */\nexport function boundaryDerivations(file: FileView, facts: Pick<ClientAuthBoundaryFacts, 'derivationSymbols' | 'authSourceModules'>): Array<{ index: number; detail: string }> {\n const { code, sourceFile } = file;\n const out: Array<{ index: number; detail: string }> = [];\n for (const site of moduleImports(sourceFile, ['import'])) {\n if (site.typeOnly) continue;\n for (const { imported, typeOnly } of site.named) {\n if (!typeOnly && facts.derivationSymbols.includes(imported)) out.push({ index: site.node.getStart(sourceFile), detail: `imports ${imported}` });\n }\n }\n const namespaces: string[] = [];\n for (const site of fromStatements(sourceFile)) {\n if (site.namespace && facts.authSourceModules.some((m) => namesModule(site.spec, m))) namespaces.push(site.namespace);\n }\n for (const namespace of namespaces) {\n for (const symbol of facts.derivationSymbols) {\n const m = new RegExp(`\\\\b${escapeRegExp(namespace)}\\\\s*\\\\.\\\\s*${escapeRegExp(symbol)}\\\\b`).exec(code);\n if (m) out.push({ index: m.index, detail: `reaches ${namespace}.${symbol}` });\n }\n }\n for (const symbol of facts.derivationSymbols) {\n const call = memberOrBareCalls(code, symbol).find((index) => !throughNamespace(code, index, namespaces));\n if (call !== undefined) out.push({ index: call, detail: `calls ${symbol}(` });\n }\n return out;\n}\n\n/** A second export of the accessor's name, or a context holding auth vocabulary, in one file. */\nexport function secondAuthAccessors(\n path: string,\n code: string,\n sourceFile: ts.SourceFile,\n facts: Pick<ClientAuthBoundaryFacts, 'accessor' | 'authContextVocabulary' | 'boundaryModules'>,\n): Array<{ index: number; detail: string }> {\n const out: Array<{ index: number; detail: string }> = [];\n const name = escapeRegExp(facts.accessor.name);\n if (path !== facts.accessor.file) {\n const exported =\n new RegExp(`\\\\bexport\\\\s+function\\\\s+${name}\\\\s*[(<]`).exec(code) ??\n new RegExp(`\\\\bexport\\\\s+(?:const|let|var)\\\\s+${name}\\\\b`).exec(code) ??\n new RegExp(`\\\\bexport\\\\s*\\\\{[^}]*\\\\b${name}\\\\b[^}]*\\\\}`).exec(code);\n if (exported) out.push({ index: exported.index, detail: `a second export of ${facts.accessor.name}` });\n }\n if (!facts.boundaryModules.some((m) => m.path === path)) {\n const vocabulary = new RegExp(`\\\\b(?:${facts.authContextVocabulary.map(escapeRegExp).join('|')})\\\\b`);\n for (const call of contextCreations(sourceFile)) {\n if (contextShapeTexts(call, sourceFile, code).some((text) => vocabulary.test(text))) {\n out.push({ index: call.getStart(sourceFile), detail: 'a second auth context' });\n }\n }\n }\n return out;\n}\n\n/** Every `createContext(…)` call in a file, bare or through a namespace. */\nfunction contextCreations(sourceFile: ts.SourceFile): ts.CallExpression[] {\n const calls: ts.CallExpression[] = [];\n forEachNode(sourceFile, (node) => {\n if (ts.isCallExpression(node) && calleeName(node) === 'createContext') calls.push(node);\n });\n return calls;\n}\n\n/**\n * The comment-free text of what a context holds: its type argument, the same-file interface or type\n * alias that argument names, its default value, and the same-file constant that value names. Only\n * these say whether the context carries auth state; the rest of the file does not.\n */\nfunction contextShapeTexts(call: ts.CallExpression, sourceFile: ts.SourceFile, code: string): string[] {\n const textOf = (node: ts.Node) => code.slice(node.getStart(sourceFile), node.getEnd());\n const declared = (name: string): ts.Node[] => {\n const found: ts.Node[] = [];\n forEachNode(sourceFile, (node) => {\n if ((ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node)) && node.name.text === name) found.push(node);\n if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.name.text === name && node.initializer) found.push(node.initializer);\n });\n return found;\n };\n const texts: string[] = [];\n for (const typeArg of call.typeArguments ?? []) {\n texts.push(textOf(typeArg));\n forEachNode(typeArg, (node) => {\n if (ts.isTypeReferenceNode(node) && ts.isIdentifier(node.typeName)) texts.push(...declared(node.typeName.text).map(textOf));\n });\n }\n const initial = call.arguments[0];\n if (initial) {\n texts.push(textOf(initial));\n if (ts.isIdentifier(initial)) texts.push(...declared(initial.text).map(textOf));\n }\n return texts;\n}\n\nexport const clientAuthBoundaryGuard: GenericGuardDefinition<ClientAuthBoundaryFacts> = {\n id: 'clientAuthBoundary',\n absentWhen: 'the app has no signed-in users',\n factProblems(facts) {\n const problems: string[] = [];\n reviewedProblems(facts?.boundaryModules, 'boundaryModules', problems);\n if (objectProblem(facts?.accessor, 'accessor', problems)) {\n textProblem(facts.accessor.file, 'accessor.file', problems);\n textProblem(facts.accessor.name, 'accessor.name', problems);\n if (Array.isArray(facts.boundaryModules) && !facts.boundaryModules.some((m) => m?.path === facts.accessor.file)) {\n problems.push(`the accessor ${facts.accessor.file} is not one of the boundaryModules`);\n }\n }\n namesProblem(facts?.derivationSymbols, 'derivationSymbols', problems);\n namesProblem(facts?.authSourceModules, 'authSourceModules', problems);\n namesProblem(facts?.authContextVocabulary, 'authContextVocabulary', problems);\n return problems;\n },\n build(facts) {\n return [\n {\n title: 'the client has one auth boundary',\n description:\n `Identity, claims, and auth loading state come from one provider, read through \\`${facts.accessor.name}\\` ` +\n `(${facts.accessor.file}). Outside the boundary modules nothing imports, reaches, or calls ` +\n `${facts.derivationSymbols.join(', ')}; no other module exports \\`${facts.accessor.name}\\` or creates a ` +\n 'context holding auth state. Read the accessor.',\n run(context) {\n const findings: Finding[] = [];\n for (const file of context.files(APP_CODE)) {\n const inBoundary = facts.boundaryModules.some((m) => m.path === file.path);\n const hits = [\n ...(inBoundary ? [] : boundaryDerivations(file, facts)),\n ...secondAuthAccessors(file.path, file.code, file.sourceFile, facts),\n ];\n for (const hit of hits) findings.push({ file: file.path, line: file.lineOf(hit.index), detail: hit.detail });\n }\n for (const module of facts.boundaryModules) {\n if (!context.reader.exists(module.path)) findings.push({ file: module.path, detail: 'stale boundary module: it no longer exists' });\n }\n if (context.reader.exists(facts.accessor.file)) {\n const code = context.file(facts.accessor.file).code;\n if (!new RegExp(`\\\\bexport\\\\s+(?:function|const)\\\\s+${escapeRegExp(facts.accessor.name)}\\\\b`).test(code)) {\n findings.push({ file: facts.accessor.file, detail: `the accessor no longer exports ${facts.accessor.name}` });\n }\n }\n return findings;\n },\n },\n ];\n },\n};\n\n// ---------------------------------------------------------------------------------------------\n// Banned auth calls: a call the auth surfaces must never make.\n\nexport interface BannedAuthCallFacts {\n /** The folders and files that make up the auth surfaces. */\n surfaces: readonly string[];\n /** Each banned call and why it is banned. */\n banned: ReadonlyArray<{ name: string; why: string }>;\n /** A long-lived auth surface the scan must reach. */\n anchor: string;\n}\n\nexport const bannedAuthCallGuard: GenericGuardDefinition<BannedAuthCallFacts> = {\n id: 'bannedAuthCalls',\n absentWhen: 'the app has no signed-in users',\n factProblems(facts) {\n const problems: string[] = [];\n namesProblem(facts?.surfaces, 'surfaces', problems);\n if (!Array.isArray(facts?.banned) || !facts.banned.length) problems.push('banned is missing or empty');\n else for (const b of facts.banned) {\n textProblem(b?.name, 'a banned call', problems);\n textProblem(b?.why, `banned call ${String(b?.name)}'s why`, problems);\n }\n textProblem(facts?.anchor, 'anchor', problems);\n if (typeof facts?.anchor === 'string' && Array.isArray(facts?.surfaces) && !facts.surfaces.some((s) => isUnder(facts.anchor, s))) {\n problems.push(`the anchor ${facts.anchor} is not inside any surface`);\n }\n return problems;\n },\n build(facts) {\n return [\n {\n title: 'the auth surfaces never name a banned auth call',\n description:\n `No file under ${facts.surfaces.join(', ')} imports or calls ` +\n facts.banned.map((b) => `\\`${b.name}\\` (${b.why})`).join('; ') +\n '. The symbol must not be reachable from these modules at all.',\n run(context) {\n const findings: Finding[] = [];\n const files = context.files(APP_CODE).filter((f) => facts.surfaces.some((s) => isUnder(f.path, s)));\n if (!files.some((f) => f.path === facts.anchor)) findings.push({ file: facts.anchor, detail: 'the anchor surface was not reached (gone, or outside the surfaces)' });\n for (const file of files) {\n for (const { name, why } of facts.banned) {\n const re = new RegExp(`\\\\b${escapeRegExp(name)}\\\\b`, 'g');\n for (let m = re.exec(file.code); m; m = re.exec(file.code)) {\n findings.push({ file: file.path, line: file.lineOf(m.index), detail: `names ${name}: ${why}` });\n }\n }\n }\n return findings;\n },\n },\n ];\n },\n};\n\n// ---------------------------------------------------------------------------------------------\n// Auth failure capture: every source calling a Firebase Auth operation that can fail reports it.\n\nexport interface AuthFailureCaptureFacts {\n /** The function every auth failure is reported through. */\n reporter: string;\n /** Sources that leave the report to the code that catches their failure, each with why and the file that reports it. */\n reportedElsewhere: ReadonlyArray<{ path: string; why: string; reportedBy: string }>;\n}\n\n/** The Firebase Auth operations that reach the service and can fail. */\nconst FAILING_AUTH_OPERATION =\n /^(?:createUserWithEmailAndPassword|signInWith\\w+|signOut|sendPasswordResetEmail|confirmPasswordReset|verifyPasswordResetCode|checkActionCode|applyActionCode|sendEmailVerification|reauthenticateWith\\w+|updatePassword|updateEmail|verifyBeforeUpdateEmail|linkWith\\w+|deleteUser)$/;\n\n/** The failing operations a source imports as values from the Firebase Auth SDK. */\nexport function failingAuthImports(file: FileView): string[] {\n const names: string[] = [];\n for (const site of moduleImports(file.sourceFile, ['import'])) {\n if (site.typeOnly || site.spec !== 'firebase/auth') continue;\n for (const { imported, typeOnly } of site.named) if (!typeOnly && FAILING_AUTH_OPERATION.test(imported)) names.push(imported);\n }\n return names;\n}\n\nexport const authFailureCaptureGuard: GenericGuardDefinition<AuthFailureCaptureFacts> = {\n id: 'authFailureCapture',\n absentWhen: 'the app has no signed-in users',\n factProblems(facts) {\n const problems: string[] = [];\n textProblem(facts?.reporter, 'reporter', problems);\n reviewedProblems(facts?.reportedElsewhere, 'reportedElsewhere', problems);\n for (const e of Array.isArray(facts?.reportedElsewhere) ? facts.reportedElsewhere : []) {\n if (e && (typeof e.reportedBy !== 'string' || !e.reportedBy.trim())) {\n problems.push(`reportedElsewhere entry ${e.path} names no reportedBy file (the file that reports its failures)`);\n }\n }\n return problems;\n },\n build(facts) {\n const reports = new RegExp(`\\\\b${escapeRegExp(facts.reporter)}\\\\s*\\\\(`);\n return [\n {\n title: 'every Firebase Auth failure reaches monitoring',\n description:\n `A source that calls a Firebase Auth operation that can fail reports its failure through \\`${facts.reporter}\\`; ` +\n 'a catch that only shows a message leaves a broken sign-up or sign-in invisible to monitoring. A source that ' +\n 'never catches the failure itself is a reviewed entry naming who reports for it; an entry that no longer calls ' +\n 'a failing operation, now reports itself, or whose reporter no longer reports is stale.',\n run(context) {\n const findings: Finding[] = [];\n const callers = context.files(APP_CODE).map((f) => ({ file: f, ops: failingAuthImports(f) })).filter((c) => c.ops.length);\n if (!callers.length) findings.push({ file: '(app)', detail: 'no source imports a failing Firebase Auth operation — the scan found no auth surface' });\n const reviewed = new Map(facts.reportedElsewhere.map((e) => [e.path, e]));\n for (const { file, ops } of callers) {\n if (!reports.test(file.code) && !reviewed.has(file.path)) findings.push({ file: file.path, detail: `calls ${ops.join(', ')} and never reports a failure` });\n }\n for (const entry of facts.reportedElsewhere) {\n const caller = callers.find((c) => c.file.path === entry.path);\n if (!caller) findings.push({ file: entry.path, detail: 'stale entry: it no longer calls a failing auth operation' });\n else if (reports.test(caller.file.code)) findings.push({ file: entry.path, detail: 'stale entry: it reports its own failures now' });\n if (!context.reader.exists(entry.reportedBy)) findings.push({ file: entry.reportedBy, detail: `the reporter named for ${entry.path} no longer exists` });\n else if (!reports.test(context.file(entry.reportedBy).code)) findings.push({ file: entry.reportedBy, detail: `the reporter named for ${entry.path} no longer reports` });\n }\n return findings;\n },\n },\n ];\n },\n};\n\n// ---------------------------------------------------------------------------------------------\n// Sign-out liveness: the \"still signed in\" check has one owner.\n\nexport interface SignOutLivenessFacts {\n /** The module that owns the liveness check. */\n owner: string;\n /** The names the owner exports for it. */\n ownerExports: readonly string[];\n /** The function that returns the client auth instance. */\n authAccessor: string;\n}\n\nexport const signOutLivenessGuard: GenericGuardDefinition<SignOutLivenessFacts> = {\n id: 'signOutLiveness',\n absentWhen: 'the app has no signed-in users',\n factProblems(facts) {\n const problems: string[] = [];\n textProblem(facts?.owner, 'owner', problems);\n namesProblem(facts?.ownerExports, 'ownerExports', problems);\n textProblem(facts?.authAccessor, 'authAccessor', problems);\n return problems;\n },\n build(facts) {\n const redeclared = new RegExp(`\\\\b${DECLARATION_KEYWORDS}\\\\s+(?:${facts.ownerExports.map(escapeRegExp).join('|')})\\\\b`, 'g');\n const negatedSkip = new RegExp(`!\\\\s*${escapeRegExp(facts.authAccessor)}\\\\(\\\\)\\\\s*\\\\??\\\\.\\\\s*currentUser`, 'g');\n return [\n {\n title: 'the signed-in check before an awaited call has one owner',\n description:\n `A check that the user is still signed in, made across an awaited hop before a call, is ${facts.owner}'s ` +\n `(${facts.ownerExports.join(', ')}). Nothing else re-declares those names or hand-rolls a negated ` +\n `\\`${facts.authAccessor}().currentUser\\` skip guard: import the owner.`,\n run(context) {\n const findings: Finding[] = [];\n for (const file of context.files(APP_CODE)) {\n if (file.path === facts.owner) continue;\n for (const re of [redeclared, negatedSkip]) {\n re.lastIndex = 0;\n for (let m = re.exec(file.code); m; m = re.exec(file.code)) {\n findings.push({ file: file.path, line: file.lineOf(m.index), detail: re === redeclared ? `re-declares ${m[0].split(/\\s+/).pop()}` : 'hand-rolls a negated currentUser skip guard' });\n }\n }\n }\n if (!context.reader.exists(facts.owner)) findings.push({ file: facts.owner, detail: 'the owner no longer exists' });\n else {\n const code = context.file(facts.owner).code;\n for (const name of facts.ownerExports) if (!declares(code, name)) findings.push({ file: facts.owner, detail: `the owner no longer declares ${name}` });\n }\n return findings;\n },\n },\n ];\n },\n};\n\n// ---------------------------------------------------------------------------------------------\n// Editor permission gate: an editor holding typed input is never mounted under a permission read.\n\nexport interface EditorPermissionGateFacts {\n /** The hook an editor calls to guard its unsaved input: a file calling it holds an editor. */\n editorMarker: string;\n /** The element the editor mounts in. */\n editorElement: string;\n /** Permission reads: identifiers starting with this prefix and a capital letter. */\n permissionNamePrefix: string;\n /** Permission reads: calls to these functions (declare [] when none). */\n permissionCalls: readonly string[];\n}\n\n/** Each editor element with an ancestor `{<permission read> && …}`. */\nexport function permissionGatedEditors(sf: ts.SourceFile, facts: Omit<EditorPermissionGateFacts, 'editorMarker'>): ts.Node[] {\n const prefix = new RegExp(`^${escapeRegExp(facts.permissionNamePrefix)}[A-Z]`);\n const readsPermission = (expr: ts.Expression) => {\n let found = false;\n const visit = (node: ts.Node) => {\n if (ts.isIdentifier(node) && prefix.test(node.text)) found = true;\n if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && facts.permissionCalls.includes(node.expression.text)) found = true;\n ts.forEachChild(node, visit);\n };\n visit(expr);\n return found;\n };\n const out: ts.Node[] = [];\n const visit = (node: ts.Node) => {\n if ((ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) && node.tagName.getText(sf) === facts.editorElement) {\n for (let p: ts.Node | undefined = node.parent; p; p = p.parent) {\n if (\n ts.isJsxExpression(p) &&\n p.expression &&\n ts.isBinaryExpression(p.expression) &&\n p.expression.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken &&\n readsPermission(p.expression.left)\n ) {\n out.push(node);\n break;\n }\n }\n }\n ts.forEachChild(node, visit);\n };\n visit(sf);\n return out;\n}\n\nexport const editorPermissionGateGuard: GenericGuardDefinition<EditorPermissionGateFacts> = {\n id: 'editorPermissionGate',\n absentWhen: false,\n factProblems(facts) {\n const problems: string[] = [];\n textProblem(facts?.editorMarker, 'editorMarker', problems);\n textProblem(facts?.editorElement, 'editorElement', problems);\n textProblem(facts?.permissionNamePrefix, 'permissionNamePrefix', problems);\n namesProblem(facts?.permissionCalls, 'permissionCalls', problems, { nonEmpty: false });\n return problems;\n },\n build(facts): Guard[] {\n return [\n {\n title: 'an editor holding typed input is never mounted under a permission gate',\n description:\n `A component that guards its unsaved input (\\`${facts.editorMarker}\\`) gates only the control that opens its ` +\n `editor on a permission; its \\`<${facts.editorElement}>\\` stays mounted, so a permission read that changes or ` +\n 'fails while it is open can never unmount it and drop what was typed. Gate the trigger, and key the editor to ' +\n 'the account that opened it.',\n run(context) {\n const findings: Finding[] = [];\n const marker = new RegExp(`\\\\b${escapeRegExp(facts.editorMarker)}\\\\s*\\\\(`);\n const editors = context.files(APP_CODE).filter((f) => marker.test(f.code));\n if (!editors.length) findings.push({ file: '(app)', detail: `no component calls ${facts.editorMarker} — the scan found no editor to check` });\n for (const file of editors) {\n for (const node of permissionGatedEditors(file.sourceFile, facts)) {\n findings.push({ file: file.path, line: file.lineOf(node.getStart(file.sourceFile)), detail: `<${facts.editorElement}> mounted under a permission gate` });\n }\n }\n return findings;\n },\n },\n ];\n },\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttt-productions/repo-guards",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/ttt-productions/ttt-packages.git",