feat(ingest): extract JS/TS arrow and function-expression definitions (#142)

* test(ingest): RED — JS/TS arrow and function-expression definitions are not extracted

Pins issue #141 in both directions: every named-value function form must be
extracted with its name, parameters and span, and the shapes carrying no
name-to-function binding (IIFE, destructured, HOC-wrapped) must stay unmatched.
Also covers the end-to-end consequence — an arrow-bound sink ranking
attack_surface, and an arrow-bound handler becoming an entry point.

Fails on main: the .js/.ts/.tsx def-queries bind declarations only.

Refs #141

* feat(ingest): extract JS/TS arrow and function-expression definitions

The .js/.ts/.tsx def-queries bound declarations only, so every definition bound
to a value produced no CodeBlock and its sinks never reached classification or
ranking. Adds the name-to-function-value shapes to all three queries:

  const f = (a) => {} / function (a) {}   variable_declarator
  const f = a => ...                      un-parenthesized single param
  obj.f = ... / module.exports.handler    assignment_expression
  onmessage = e => ...                    bare-identifier assignment
  { f: (a) => {} }                        pair
  class K { f = (a) => {} }               field_definition / public_field_definition

Query-only — no parser, pipeline or CodeBlock-shape change. nodeToCodeBlock
already handles any @def/@name/@params triple and kindOf maps these to
'function'. Property-assigned and object-literal forms are named by the
property, consistent with how method_definition is named: that is the name
buildCallGraph and the entry-point heuristic match on.

Not matched, by design: HOC-wrapped values (const h = withAuth(fn)) and
anonymous callbacks (app.get('/x', (req,res) => ...)) have no name-to-function
binding, and inventing one is worse than honest absence. whitebox.ts caveat
block updated to match.

Refs #141

* fix(ingest): capture #private class-field arrows; keep same-line block ids distinct

Two defects found by adversarial review of the arrow-extraction change:

- `class C { #handler = (x) => eval(x) }` yielded nothing: a private field's
  name is a `private_property_identifier`, not the `property_identifier` the
  class-field pattern required. A sink in a private handler is exactly as
  dangerous as one in a public handler, so it is captured (name keeps the `#`).

- A block id is `path::name@line`, so two same-named definitions starting on the
  same line collide, and buildCallGraph keys on that id — the two blocks silently
  share one callers/callees entry even when only one carries a sink. Rare for
  declarations, routine once value-bound functions are extracted (minified
  bundles pack many `{ handler: … }` onto one line). The later block is now
  disambiguated by column; the first keeps the plain id, so no existing id
  changes and the Python path is untouched.

Refs #141
This commit is contained in:
Lyubomir Bozhinov authored and GitHub committed 2026-08-01 16:05:29 -04:00
1 parent a606adf038
commit 9020e5fc08
4 files changed
+304 -8

No files matched your search

+6 -4
View File
@@ -16,12 +16,14 @@
* or code imported from outside the scan scope, is invisible. Do NOT describe this as
* "any repo" or "any language".
*
* KNOWN v1 EXTRACTION LIMITS (fail-safe — these shapes are silently NOT extracted or
* KNOWN EXTRACTION LIMITS (fail-safe — these shapes are silently NOT extracted or
* under-linked, never mis-extracted; all strictly better than the prior Python-only
* ingest, and tracked as follow-ups):
* - 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.
* - JS/TS: declarations plus name-bound function values (`const f = () => …`,
* `obj.f = …`, `{ f: … }`, class-field arrows incl. `#private`) are captured.
* Definitions with no name-to-function binding are not: HOC-wrapped values
* (`const h = withAuth(fn)`) and anonymous callbacks
* (`app.get('/x', (req, res) => …)`).
* - C++: only free functions are captured. In-class and out-of-line (`Class::method`)
* member methods are not — a materially narrower C++ story than "full support".
* - Entry-point elevation for non-Python code relies on name heuristics only; no