Skip to content

fix(lsp): judge Express middleware on its own body, not its module - #168

Merged
jaurakunal merged 1 commit into
mainfrom
fix/express-scope
Sep 5, 2026
Merged

fix(lsp): judge Express middleware on its own body, not its module#168
jaurakunal merged 1 commit into
mainfrom
fix/express-scope

Conversation

@jaurakunal

Copy link
Copy Markdown
Owner

The last instance of a weakness fixed twice already elsewhere.

The bug

_trace_express_auth resolved a middleware to its definition and then searched the whole definition file for an auth terminal. Auth helpers cluster in one module, so any middleware resolving into that module inherited its siblings' terminals:

export function requireAuth(req, res, next) {
  next()                                    // ← verifies nothing
}

export function checkToken(req, res, next) {
  const user = jwt.verify(req.headers.authorization, SECRET)   // ← but this is in the same file
}

A route guarded only by requireAuth was reported as authenticated — and a route reported as authenticated has its missing-auth finding suppressed, so the failure direction is hiding a vulnerability.

Same shape as the inline path (a login route importing signToken from the module that defines verifyToken). _trace_definition_body returns only the resolved symbol's own declaration, and both _find_auth_terminal and _has_enforcement now see just that.

Also: resolving from the import line

re.search found the middleware's first occurrence in the file — usually its import. Asking for the definition of an import specifier is answered with the import itself, which is exactly the shape the project-load retry waits on, so these traces were spending retries before failing. _locate_symbol prefers a use site.

Scope

This path is narrower than it was: centrally-mounted routes are attributed per route since #163, so it now serves files applying middleware inline. Its hardcoded list of five middleware names is untouched — it still can't name a project's own vocabulary, which is worth generalising but is a change of behaviour rather than a correction.

Verification

Unit suite 2368 passed (+2)
sast-injection 46/46, 0 FP
VAmPI 3/3
test-app 110 — 4 routes verified, 4 suppressed
juice-shop 183 — 17 routes verified, 15 suppressed

Findings unchanged everywhere, as expected: neither target reaches this path any more. The two new tests fail if the whole-file scan is put back — verified by reverting it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EZ4QoTqRoYWE25CNoV2Wfy

`_trace_express_auth` resolved a middleware to its definition and then
searched the whole definition *file* for an auth terminal. Auth helpers
cluster in one module, so any middleware resolving into that module inherited
its siblings' terminals — a `requireAuth` that only calls `next()` was
confirmed as verifying auth because a `checkToken` further down the same file
calls `jwt.verify`.

This is the weakness already fixed for the inline path, where a login route
importing `signToken` from the module that also defines `verifyToken` came out
authenticated. `_trace_definition_body` returns only the resolved symbol's own
declaration, and both `_find_auth_terminal` and `_has_enforcement` now see
just that.

Also stop resolving from the import line. `re.search` found the middleware's
first occurrence in the file, which is usually its import, and asking for the
definition of an import specifier is answered with the import itself — the
shape the project-load retry waits on. `_locate_symbol` prefers a use site.

The path is narrower than it was: centrally-mounted routes are attributed per
route, so this now serves files that apply middleware inline. Its hardcoded
list of five middleware names is untouched and still cannot name a project's
own vocabulary — worth generalising, but a change of behaviour rather than a
correction.

Findings are unchanged everywhere: test-app 110 with 4 suppressed, juice-shop
183 with 15, benchmarks 46/46 with 0 FP and 3/3 on VAmPI. The two new tests
fail if the whole-file scan is put back.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ4QoTqRoYWE25CNoV2Wfy
@jaurakunal
jaurakunal merged commit 7561793 into main Sep 5, 2026
8 checks passed
@jaurakunal
jaurakunal deleted the fix/express-scope branch September 5, 2026 08:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant