diff --git a/scripts/verify/rea-numeric-limits.mjs b/scripts/verify/rea-numeric-limits.mjs index 68a2244991..e000687a5e 100644 --- a/scripts/verify/rea-numeric-limits.mjs +++ b/scripts/verify/rea-numeric-limits.mjs @@ -93,13 +93,59 @@ export const sourceForLimitDiscovery = (source) => { return out; }; -/** A quoted name in an array is an allowlist entry, not a limit producer. */ +const ARRAY_KEYWORDS = new Set([ + "return", + "const", + "let", + "var", + "of", + "in", + "case", + "throw", + "yield", + "await", + "typeof", + "void", + "new", + "delete", + "if", + "else", + "for", + "while", + "do", + "switch", + "export", + "default", + "as", + "satisfies", +]); + +const identifierEndingAt = (source, index) => { + let cursor = index; + while (cursor >= 0 && /\s/u.test(source[cursor] ?? "")) cursor -= 1; + const end = cursor; + while (cursor >= 0 && /[A-Za-z0-9_$]/u.test(source[cursor] ?? "")) + cursor -= 1; + return source.slice(cursor + 1, end + 1); +}; + +/** + * A quoted name in a declaration array is an allowlist entry. + * `environment["REA_..."]` and `value?.["REA_..."]` are producers. + */ const isArrayElementLiteral = (source, index) => { if (source[index - 1] !== '"') return false; let cursor = index - 2; while (cursor >= 0 && /\s/u.test(source[cursor] ?? "")) cursor -= 1; const previous = source[cursor]; - return previous === "[" || previous === ","; + if (previous === ",") return true; + if (previous !== "[") return false; + let before = cursor - 1; + while (before >= 0 && /\s/u.test(source[before] ?? "")) before -= 1; + const mark = source[before]; + if (mark === "?" || mark === ")" || mark === "]") return false; + const token = identifierEndingAt(source, cursor - 1); + return token.length === 0 || ARRAY_KEYWORDS.has(token); }; export const isNumericLimit = (name) => diff --git a/tests/evaluation/numericLimitCheck.test.ts b/tests/evaluation/numericLimitCheck.test.ts index 21eeafb6f6..af9792aacd 100644 --- a/tests/evaluation/numericLimitCheck.test.ts +++ b/tests/evaluation/numericLimitCheck.test.ts @@ -41,6 +41,17 @@ it("does not treat a registry declaration as a producer", () => { ).toBe(false); }); +it("keeps a bracket-access read as a producer", () => { + const name = "REA_NEW_MAX_BYTES"; + const discovered = discoverNumericLimitUses([ + { + path: "src/config/newLimit.ts", + source: `const selected = environment["${name}"] ?? environment?.[ "${name}" ];`, + }, + ]); + expect(discovered.get(name)).toBe("src/config/newLimit.ts"); +}); + it("does not treat a registration allowlist or a comment as a producer", () => { const discovered = discoverNumericLimitUses([ {