Skip to content

feat(entrypoints): rules engine + shipped ruleset, at parity with python #72

Description

@rahlk

Problem

TypeScript has no entrypoint detection. grep -rniE "entry_?point" src/ returns one hit, and it
is a bundler comment in src/dataflow/pool.ts. The fields were de-advertised in #60 precisely
because nothing populated them; this issue is the work that earns them back.

Without it the analyzer emits a call graph with no distinguished roots, so a consumer cannot ask
what is reachable from outside the application — the first question any taint or attack-surface
query asks.

codeanalyzer-python shipped this as its #27: a codeanalyzer/entrypoints/ package (5 files), 73
source references, PyEntrypoint/PyEntrypointReport, is_entrypoint and entrypoint_frameworks
on two Neo4j labels, and --entrypoint-rules.

Design: docs/design/specs/entrypoint-detection.md.

Scope boundary

This issue is a rules ENGINE plus a shipped ruleset, not a pair of framework finders. Its
previous title ("entrypoint finders (Express/Angular routes)") named two frameworks and no engine;
built that way it would produce hardcoded detectors with no rules file, no confidence grading, no
coverage report and no user extensibility, then need rewriting to reach parity.

The other half of #60 is already done and is NOT in scope. Top-level invocation roots are in the
call graph: module-scope calls are attributed to the MODULE (src/semantic_analysis/callGraph.ts:161,
python #131 parity). Verified — a module-scope boot() now yields can://…/app.ts -> can://…/app.ts/boot
with prov: ["tsc"].

Not in scope: taint or reachability queries over the entrypoints (those are frontend SDK concerns).

Goals

Per the spec's decomposition; each becomes its own PR, filed just-in-time.

  • TSEntrypoint/TSEntrypointReport schema, Neo4j is_entrypoint/entrypoint_frameworks, and
    the level-free L1 post-pass skeleton emitting an empty report
  • Stage-0 framework gate over TSImportTSDependency, populating frameworks_detected
  • Rules file format, loader, --entrypoint-rules, and the decorator matcher (ships NestJS +
    Angular)
  • Base-class matcher over resolved extends_ids/implements_ids, with dispatch: and via:
  • Call-site, file-convention and manifest matchers — the TypeScript-specific ones

Caveats and known risks

Python's engine cannot be ported as-is. It has two matchers, decorators and base classes, which
cover its ecosystem. TypeScript needs three more with no python analog:

framework declared how matcher
NestJS, Angular decorators python has it
Express, Koa, Fastify app.get('/p', h) — a call call-site
Next.js, Remix, SvelteKit app/**/route.ts, +server.ts file convention
AWS Lambda exported handler export name
CLI tools, packages bin/main in package.json manifest

The rules format must be designed for those from the start, not copied from python's rules.yml.

  • Under-approximation is the designed failure mode and is invisible without the report. Ship
    TSEntrypointReport in the same change as detection, never after — a framework the ruleset misses
    looks identical to a project with no entrypoints.
  • False positives are worse than misses. A locally defined Controller decorator in a non-NestJS
    project must not register. The stage-0 gate is what prevents that, so it is not an optimisation.
  • The call-site matcher is the weakest link; it should ship at confidence: heuristic. Express
    handlers registered through a variable (const r = express.Router()) or a helper will be missed,
    and that belongs in the report's unresolved counts.
  • Two decisions are open in the spec: whether a file-convention entrypoint may hang off a module
    (python has no module-level entrypoints, so this diverges from the shared vocabulary and should go
    to the siblings first), and how call-site matching stays level-free — the spec recommends a
    syntactic L1 match at heuristic rather than gating Express to -a >= 2, which would break
    "identical at every -a".
  • File-convention rules are framework-version-sensitive — Next.js moved pages/api to
    app/**/route.ts across a major. Rules must express both without a code change or the ruleset rots.

Definition of done

  • A NestJS or Angular project gets correct, gated, reported entrypoints — not "some entrypoints were
    found"
  • SCHEMA_VERSION unmoved; the change is provably additive (existing fields and relationships
    unchanged)
  • Detection never aborts an analysis: rule loading is a hard error before analysis starts, detection
    itself is best-effort
  • Entrypoints are identical at every -a, and a test asserts it
  • A project that does NOT use a framework registers zero of its entrypoints (the gate is tested, not
    assumed)
  • TSEntrypointReport is populated in the same release as the detection it reports on
  • Validated against real projects, not only fixtures — python's ruleset was tuned that way, and
    "it found entrypoints on vscode" is not validation

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions