Skip to content

Framework-independent entrypoint detection (JackEE-style finders) #27

Description

@rahlk

Problem

Nothing in the emitted schema says which callables a framework invokes from outside the
application's own call graph. A call graph without identified roots cannot answer reachability,
dead-code, or attack-surface questions, and every consumer re-derives entrypoints ad hoc.

The gap is load-bearing downstream. Java emits is_entrypoint and is_entrypoint_class
(python-sdk/cldk/models/java/models.py:343,403), so JavaAnalysis.get_entry_point_* are real
methods. The Python facade mirrors the same surface but all four
get_entry_point_classes, get_entry_point_methods, get_service_entry_point_classes,
get_service_entry_point_methods — raise NotImplementedError
(python-sdk/cldk/analysis/python/python_analysis.py:881-960), purely because the backend
supplies no data.

Scope boundary

Entrypoints only. CRUD detection is specced separately. is_entrypoint_class covers
inheritance-based entrypoints only, not a coarse "worth analyzing" filter as in Java — in Python
the function-level predicate does the real work.

Goals

  • Schema, all defaulted: PyClass.is_entrypoint, PyClass.entrypoint_framework, PyCallable.is_entrypoint, PyCallable.entrypoint_framework
  • frameworks/_base.py: AbstractEntrypointFinder plus ModuleContext carrying per-project routing facts
  • Per-framework finders behind entrypoint_factory.py, OR-ing results (mirrors Java's EntrypointsFinderFactory)
  • Routing pre-pass in frameworks/routing/, one pass per project, emitting {qualified_name → route_metadata}
  • Wire into symbol_table_builder.py: routing pre-pass first, then set the fields during class/function construction
Finder Signals
flask @app.route, @bp.get|post|…, MethodView/View bases
fastapi @app|router.get|post|websocket, router mounts via ModuleContext
django CBV bases, DRF @api_view/@action, urls.py resolution
tornado RequestHandler subclass
celery @app.task, @shared_task, @periodic_task
aws_lambda handler(event, context) + SAM/serverless binding
cli @click.command, @app.command
grpc *Servicer subclass

Routing resolvers: django_url_resolver (path/re_path/url/include chains, .as_view()),
fastapi_router_resolver (include_router(prefix=…), mount), flask_blueprint_resolver
(Blueprint + register_blueprint).

Caveats and known risks

  • The routing pre-pass is the only piece with no Java analog and carries most of the risk.
  • Decorator-only detection was rejected: it misses Django entirely, where views bind in urls.py.
  • Literal JackEE (Datalog/Doop fact ingestion) was rejected: this analyzer is AST-based, and Java already ported the same architecture to ASTs.
  • Resolving in the SDK was rejected: it sees only the serialized schema and cannot re-run AST passes.
  • Tree-sitter suffices for decorators and base classes; Jedi is needed only for cross-module base resolution.

Definition of done

  • A Flask @app.route view, a FastAPI @router.get handler, a Celery @shared_task, and a Click @cli.command each carry is_entrypoint=True and the right entrypoint_framework
  • A Django function view with no decorator, bound only via path('...', view) through one include(), is flagged — the routing pre-pass gate
  • An internally-called helper is not flagged
  • Pre-existing analysis.json files still load
  • PythonAnalysis.get_entry_point_* become thin readers over these fields, matching JavaAnalysis

Reference

JackEE — Antoniadis et al., "Static Analysis of Java Enterprise Applications", PLDI 2020.
Java mirror: src/main/java/com/ibm/cldk/javaee/.

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