Skip to content

White-box ingest: JS/TS arrow functions and function expressions are never extracted #141

Description

@lyubomir-bozhinov

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions