Summary
White-box ingest extracts JS/TS declarations only (function foo(), class C, method()). Definitions bound to a value — arrow functions and function expressions — are silently not extracted, so their bodies never reach sink classification, priority scoring, or the context pack.
In modern Node/TS this is the dominant shape for exactly the code an operator cares about: Express handlers, Lambda entry points, exported utilities.
const runCmd = (cmd) => exec(cmd); // not extracted -> exec() sink unranked
module.exports.handler = async (e) => run(e); // not extracted -> Lambda entry point invisible
const routes = { upload: (p) => open(p) }; // not extracted
This is a known limit of the multi-language ingest, documented in-code at the time it landed (#75) and tracked here as the follow-up.
Where
src/recon/ts-grammars.ts — the .js / .ts / .tsx tree-sitter def-queries bind only:
(function_declaration ...) @def
(method_definition ...) @def
(class_declaration ...) @def
There is no pattern for arrow_function / function_expression bound to a name, so parseFileMultiLang returns nothing for those regions and the whole downstream pipeline (classify -> prioritize -> findEntryPoints -> reachability -> context pack) never sees them.
Reference — the caveat block in src/recon/whitebox.ts:
JS/TS: only function/method/class declarations are captured. Arrow-function and function-expression definitions (const f = () => …) are not — idiomatic in modern TS, so their sinks may go unranked.
Impact
- Missed attack surface. A file whose every function is
const f = (x) => … currently ingests as zero blocks. Sinks in it (exec, eval, fetch, open) are never classified attack_surface.
- Missed entry points.
findEntryPoints matches on name (/^(handle|on|process|serve|…)/). module.exports.handler = async (e) => … never produces a block, so it can never become an entry point — and nothing downstream of it is reachable.
- Silent, not noisy: the file parses fine, it just yields no blocks. Nothing in the output indicates the coverage hole.
Proposed fix
Extend the .js / .ts / .tsx def-queries with the named-value function forms. No parser, pipeline, or CodeBlock-shape change — nodeToCodeBlock already handles any captured @def/@name/@params triple, and kindOf maps these to function.
Forms to cover:
| Form |
Node |
const f = (a) => {} / const f = function (a) {} |
variable_declarator |
const f = a => … (un-parenthesized single param) |
variable_declarator |
obj.f = (a) => {}, module.exports.handler = … |
assignment_expression (name = the property, consistent with how method_definition is named) |
{ f: (a) => {} } |
pair |
class K { f = (a) => {} } |
field_definition (js) / public_field_definition (ts, tsx) |
Deliberately out of scope (would need a name-inference heuristic rather than a query, and are better as their own follow-up):
- HOC-wrapped definitions —
export const h = withAuth(async (req) => …) — the value is a call_expression, so there is no direct name-to-function binding.
- Anonymous callbacks —
app.get('/x', (req, res) => …) — no name to bind. (Related but separate: no decorator/route-registration entry-point elevation, also noted in the whitebox.ts caveat block.)
Verification plan
RED test first: fixtures per language asserting each form is extracted with the right name/params/line, an end-to-end assertion that an arrow-bound sink ranks attack_surface and an arrow-bound handler is picked up as an entry point, plus negative assertions that non-function initializers (const x = 42), IIFEs, and destructured bindings are not matched, and that no existing declaration form double-extracts.
I hit this while working on #75 and have the fix. Happy to take it if no one else is on it.
Summary
White-box ingest extracts JS/TS declarations only (
function foo(),class C,method()). Definitions bound to a value — arrow functions and function expressions — are silently not extracted, so their bodies never reach sink classification, priority scoring, or the context pack.In modern Node/TS this is the dominant shape for exactly the code an operator cares about: Express handlers, Lambda entry points, exported utilities.
This is a known limit of the multi-language ingest, documented in-code at the time it landed (#75) and tracked here as the follow-up.
Where
src/recon/ts-grammars.ts— the.js/.ts/.tsxtree-sitter def-queries bind only:There is no pattern for
arrow_function/function_expressionbound to a name, soparseFileMultiLangreturns nothing for those regions and the whole downstream pipeline (classify->prioritize->findEntryPoints->reachability-> context pack) never sees them.Reference — the caveat block in
src/recon/whitebox.ts:Impact
const f = (x) => …currently ingests as zero blocks. Sinks in it (exec,eval,fetch,open) are never classifiedattack_surface.findEntryPointsmatches on name (/^(handle|on|process|serve|…)/).module.exports.handler = async (e) => …never produces a block, so it can never become an entry point — and nothing downstream of it is reachable.Proposed fix
Extend the
.js/.ts/.tsxdef-queries with the named-value function forms. No parser, pipeline, orCodeBlock-shape change —nodeToCodeBlockalready handles any captured@def/@name/@paramstriple, andkindOfmaps these tofunction.Forms to cover:
const f = (a) => {}/const f = function (a) {}variable_declaratorconst f = a => …(un-parenthesized single param)variable_declaratorobj.f = (a) => {},module.exports.handler = …assignment_expression(name = the property, consistent with howmethod_definitionis named){ f: (a) => {} }pairclass K { f = (a) => {} }field_definition(js) /public_field_definition(ts, tsx)Deliberately out of scope (would need a name-inference heuristic rather than a query, and are better as their own follow-up):
export const h = withAuth(async (req) => …)— the value is acall_expression, so there is no direct name-to-function binding.app.get('/x', (req, res) => …)— no name to bind. (Related but separate: no decorator/route-registration entry-point elevation, also noted in thewhitebox.tscaveat block.)Verification plan
RED test first: fixtures per language asserting each form is extracted with the right name/params/line, an end-to-end assertion that an arrow-bound sink ranks
attack_surfaceand an arrow-boundhandleris picked up as an entry point, plus negative assertions that non-function initializers (const x = 42), IIFEs, and destructured bindings are not matched, and that no existing declaration form double-extracts.I hit this while working on #75 and have the fix. Happy to take it if no one else is on it.