Skip to content

Express: propagate prefix-less use() middleware with call-order semantics; emit app.all() catch-all routes #109

Description

@bjcorder

Problem

Two documented Express gaps (docs/PARSERS_AND_ADAPTERS.md, "Express — Not yet") affect classification accuracy:

  1. Prefix-less app.use(mw) / router.use(mw) is the single most common way Express apps apply auth middleware globally (app.use(requireAuth) before route definitions). Because it is not propagated, every route in such apps is reported as unguarded — a systematic false negative on the most popular pattern.
  2. app.all(path, handler) with a terminal handler is a real route responding to every HTTP method, but it is currently collected as prefix middleware, so it never appears in the route inventory.

Current behavior

  • ExpressAdapter (crates/authmap-adapters/src/lib.rs) handles method calls on app/router receivers, mounted routers with prefix composition, route-level and array middleware, and chaining.
  • .use(...) and .all(...) both flow into collect_prefix_middleware (crates/authmap-adapters/src/lib.rs, see the dispatch around line ~3264); middleware without a path prefix is not applied to sibling routes, and .all is never emitted as a route.

Proposed implementation

  1. Prefix-less use() propagation with call-order semantics
    • Express applies middleware only to routes registered after the use() call on the same app/router object. Record the source byte offset of each use() and each route registration (spans are already tracked for middleware calls); when attaching evidence, apply a prefix-less middleware to a route iff use_offset < route_offset and both are on the same receiver binding.
    • Classify the middleware symbol via the existing evidence-rule matching (passport rule names etc. are already supported). Unrecognized middleware: leave coverage unclaimed and record it in the route's uncertainty/rationale rather than claiming a guard.
    • Same-file scope first; middleware applied in an entry file to routers mounted from other files should flow through the existing mount composition (mounted-router routes inherit entry-file use() calls that precede the app.use('/prefix', router) mount).
  2. app.all() terminal handlers as catch-all routes
    • Distinguish all() by argument shape: if the final argument is a terminal handler (function/identifier that resolves to a handler, not a known middleware chain continuation), emit a route with method * (or an ALL marker consistent with the core Route model) instead of treating it as middleware. Path-less app.all(handler) stays middleware-like.
    • Out of scope for this issue (documented low-impact): app.param() and dynamic app[method]() dispatch — keep them in the Not yet list.

Acceptance criteria

  • Fixture: app.use(requireAuth) followed by routes → routes classified as guarded; a route declared before the use() call remains unguarded (order semantics test).
  • Fixture: mounted router inherits entry-file global auth middleware registered before the mount.
  • Fixture: app.all('/admin/*', handler) appears in the route inventory as a catch-all route with evidence extracted from the handler chain.
  • Unrecognized global middleware does not fabricate guard evidence (conservative default).
  • Unit tests + golden fixture updates; docs/PARSERS_AND_ADAPTERS.md Express Not yet list updated; CHANGELOG entry.

References

  • docs/PARSERS_AND_ADAPTERS.md Express limitations paragraph
  • crates/authmap-adapters/src/lib.rs (ExpressAdapter, collect_prefix_middleware)

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:adapterFramework adapter workdetectionAnalyzer detection behavior or pattern coverageenhancementNew feature or requestframework:expressExpress supportrustRust implementation work

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions