Problem
Two documented Express gaps (docs/PARSERS_AND_ADAPTERS.md, "Express — Not yet") affect classification accuracy:
- 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.
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
- 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).
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
References
docs/PARSERS_AND_ADAPTERS.md Express limitations paragraph
crates/authmap-adapters/src/lib.rs (ExpressAdapter, collect_prefix_middleware)
Problem
Two documented Express gaps (
docs/PARSERS_AND_ADAPTERS.md, "Express — Not yet") affect classification accuracy: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.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 intocollect_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.allis never emitted as a route.Proposed implementation
use()propagation with call-order semanticsuse()call on the same app/router object. Record the source byte offset of eachuse()and each route registration (spans are already tracked for middleware calls); when attaching evidence, apply a prefix-less middleware to a route iffuse_offset < route_offsetand both are on the same receiver binding.use()calls that precede theapp.use('/prefix', router)mount).app.all()terminal handlers as catch-all routesall()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 anALLmarker consistent with the coreRoutemodel) instead of treating it as middleware. Path-lessapp.all(handler)stays middleware-like.app.param()and dynamicapp[method]()dispatch — keep them in the Not yet list.Acceptance criteria
app.use(requireAuth)followed by routes → routes classified as guarded; a route declared before theuse()call remains unguarded (order semantics test).app.all('/admin/*', handler)appears in the route inventory as a catch-all route with evidence extracted from the handler chain.docs/PARSERS_AND_ADAPTERS.mdExpress Not yet list updated; CHANGELOG entry.References
docs/PARSERS_AND_ADAPTERS.mdExpress limitations paragraphcrates/authmap-adapters/src/lib.rs(ExpressAdapter,collect_prefix_middleware)