JS: Add support for @vercel/node serverless functions#21697
JS: Add support for @vercel/node serverless functions#21697murderteeth wants to merge 8 commits intogithub:mainfrom
Conversation
This adds a framework model for Vercel serverless functions so that
CodeQL's existing JavaScript security queries can detect vulnerabilities
in handlers of the form
export default function handler(req: VercelRequest, res: VercelResponse) { ... }
Handlers are identified as the default export of a module whose first
two parameters are typed as `VercelRequest`/`VercelResponse` from
`@vercel/node`. The default-export constraint excludes private helpers
that share the same signature. Type-based detection follows the same
pattern already used by `NextReqResHandler` in `Next.qll`.
The framework model covers:
- Route handler recognition (default-exported typed handlers only)
- Request input sources: `query`, `body`, `cookies`, and `url`
(the last inherited from Node's `IncomingMessage`)
- Named header accesses like `req.headers.host` and `req.headers.referer`,
modelled as `Http::RequestHeaderAccess` so header-specific queries fire
- Response sinks: `res.send`, `res.status(...).send`, `res.redirect`
- Header definitions via `res.setHeader`
Includes a library test exercising each model predicate (including a
negative case for private helpers) and query consistency fixtures
demonstrating end-to-end detection for js/reflected-xss,
js/request-forgery, js/sql-injection, and js/command-line-injection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a new JavaScript framework model for @vercel/node Vercel serverless functions so existing CodeQL HTTP/dataflow-based security queries can recognize Vercel API handlers and treat req/res operations as sources/sinks.
Changes:
- Introduces
VercelNodeframework modeling library and wires it intojavascript.qll. - Adds library-tests for the new framework model plus several query-test fixtures/expected updates to demonstrate end-to-end findings.
- Updates changelog/change-note and supported-frameworks documentation to reflect the new support.
Reviewed changes
Copilot reviewed 24 out of 24 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| javascript/ql/lib/semmle/javascript/frameworks/VercelNode.qll | Adds the Vercel Node framework model (route handler identification + HTTP sources/sinks). |
| javascript/ql/lib/javascript.qll | Imports the new VercelNode framework model so it’s included in the JS library pack. |
| javascript/ql/lib/change-notes/2026-04-12-vercel-node.md | Announces the new @vercel/node support in the JS library pack change notes. |
| docs/codeql/reusables/supported-frameworks.rst | Documents the new supported framework entry. |
| javascript/ql/test/library-tests/frameworks/vercel/tests.ql | Aggregates the Vercel framework library-tests. |
| javascript/ql/test/library-tests/frameworks/vercel/tests.expected | Expected results for the Vercel framework library-tests. |
| javascript/ql/test/library-tests/frameworks/vercel/src/vercel.ts | Positive test case covering request inputs and response APIs. |
| javascript/ql/test/library-tests/frameworks/vercel/src/notahandler.ts | Negative test case ensuring non-matching parameter positions don’t qualify as a handler. |
| javascript/ql/test/library-tests/frameworks/vercel/RouteHandler.qll | Library-test query for VercelNode::RouteHandler. |
| javascript/ql/test/library-tests/frameworks/vercel/RequestSource.qll | Library-test query for request parameter source modeling. |
| javascript/ql/test/library-tests/frameworks/vercel/ResponseSource.qll | Library-test query for response parameter/chained response source modeling. |
| javascript/ql/test/library-tests/frameworks/vercel/RequestInputAccess.qll | Library-test query for request input access kinds (query/body/cookies/url/headers). |
| javascript/ql/test/library-tests/frameworks/vercel/HeaderDefinition.qll | Library-test query for response header definition modeling. |
| javascript/ql/test/library-tests/frameworks/vercel/ResponseSendArgument.qll | Library-test query for res.send(...) sink modeling. |
| javascript/ql/test/library-tests/frameworks/vercel/RedirectInvocation.qll | Library-test query for res.redirect(...) modeling. |
| javascript/ql/test/query-tests/Security/CWE-918/vercel.ts | Query-test fixture demonstrating request-forgery detection in a Vercel handler. |
| javascript/ql/test/query-tests/Security/CWE-918/RequestForgery.expected | Updated expected results including the new Vercel fixture findings. |
| javascript/ql/test/query-tests/Security/CWE-089/untyped/vercel.ts | Query-test fixture demonstrating SQL injection detection in a Vercel handler. |
| javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected | Updated expected results including the new Vercel fixture findings. |
| javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/vercel.ts | Query-test fixture demonstrating reflected XSS detection in a Vercel handler. |
| javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXssWithCustomSanitizer.expected | Updated expected results including the new Vercel fixture findings. |
| javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXss.expected | Updated expected results including the new Vercel fixture findings. |
| javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/vercel.ts | Query-test fixture demonstrating command injection detection in a Vercel handler. |
| javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/CommandInjection.expected | Updated expected results including the new Vercel fixture findings. |
| * `VercelResponse` from `@vercel/node`. | ||
| * | ||
| * Since `@vercel/node` is commonly imported as a type-only import, handlers | ||
| * are recognised by their TypeScript parameter types. The default-export |
There was a problem hiding this comment.
Spelling consistency: this file uses British spelling (“recognised”), but the surrounding JavaScript framework libraries consistently use US spelling (“recognized”). Please change to “recognized” here (and keep wording consistent across related docs/tests).
| * are recognised by their TypeScript parameter types. The default-export | |
| * are recognized by their TypeScript parameter types. The default-export |
| * with signature `(req: VercelRequest, res: VercelResponse) => void`, where | ||
| * the types are imported from the `@vercel/node` package. The Vercel runtime | ||
| * invokes the default export for every incoming HTTP request. |
There was a problem hiding this comment.
The documentation comment says the handler signature returns void, but handlers can be async/return a Promise (and the PR’s own fixtures include an async function handler). Please update the comment to avoid implying a synchronous-only handler.
| * with signature `(req: VercelRequest, res: VercelResponse) => void`, where | |
| * the types are imported from the `@vercel/node` package. The Vercel runtime | |
| * invokes the default export for every incoming HTTP request. | |
| * taking parameters `(req: VercelRequest, res: VercelResponse)`, where the | |
| * types are imported from the `@vercel/node` package. The default export may | |
| * be synchronous or `async`, and the Vercel runtime invokes it for every | |
| * incoming HTTP request. |
| --- | ||
| category: newFeature | ||
| --- | ||
| * Added support for [`@vercel/node`](https://www.npmjs.com/package/@vercel/node) Vercel serverless functions. Handlers are recognised via the `VercelRequest`/`VercelResponse` TypeScript parameter types, and standard security queries (`js/reflected-xss`, `js/request-forgery`, `js/sql-injection`, `js/command-line-injection`, etc.) now detect vulnerabilities in Vercel API route files. |
There was a problem hiding this comment.
Spelling: change “recognised” to “recognized” for consistency with other JavaScript change notes (which use US spelling).
| * Added support for [`@vercel/node`](https://www.npmjs.com/package/@vercel/node) Vercel serverless functions. Handlers are recognised via the `VercelRequest`/`VercelResponse` TypeScript parameter types, and standard security queries (`js/reflected-xss`, `js/request-forgery`, `js/sql-injection`, `js/command-line-injection`, etc.) now detect vulnerabilities in Vercel API route files. | |
| * Added support for [`@vercel/node`](https://www.npmjs.com/package/@vercel/node) Vercel serverless functions. Handlers are recognized via the `VercelRequest`/`VercelResponse` TypeScript parameter types, and standard security queries (`js/reflected-xss`, `js/request-forgery`, `js/sql-injection`, `js/command-line-injection`, etc.) now detect vulnerabilities in Vercel API route files. |
| @@ -0,0 +1,27 @@ | |||
| import type { VercelRequest, VercelResponse } from "@vercel/node"; | |||
|
|
|||
| // A private helper with the same signature. Must NOT be recognised as a | |||
There was a problem hiding this comment.
Spelling in comment: change “recognised” to “recognized” to match the rest of the JavaScript codebase’s comment spelling.
| // A private helper with the same signature. Must NOT be recognised as a | |
| // A private helper with the same signature. Must NOT be recognized as a |
|
|
||
| // A default-exported function that has VercelRequest/VercelResponse at | ||
| // positions 1 and 2, not 0 and 1. Vercel does not invoke it this way, | ||
| // so it must NOT be recognised as a route handler. |
There was a problem hiding this comment.
Spelling in comment: change “recognised” to “recognized” to match the rest of the JavaScript codebase’s comment spelling.
| // so it must NOT be recognised as a route handler. | |
| // so it must NOT be recognized as a route handler. |
| superagent, Network communicator | ||
| swig, templating language | ||
| underscore, Utility library | ||
| vercel, Serverless framework |
There was a problem hiding this comment.
This row says “vercel”, but the added modeling is specifically for @vercel/node serverless functions. Consider renaming the entry to be more specific (for example, “Vercel (@vercel/node)” or similar) to avoid implying broader Vercel platform coverage.
| vercel, Serverless framework | |
| Vercel (@vercel/node), Serverless framework |
asgerf
left a comment
There was a problem hiding this comment.
Thanks! Simple and clean. I have one potential improvement, otherwise looks good.
Extends the Vercel serverless handler detection to also match the deprecated Zeit-era @now/node package with NowRequest/NowResponse types. Per-review feedback from asgerf, these aliases still appear in real-world code. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fixes US spelling (recognised -> recognized) across docs, QLDoc, change note, and test fixture comments. Clarifies the handler QLDoc to note sync/async support. Renames the supported-frameworks entry from "vercel" to "Vercel (@vercel/node)" to avoid implying broader platform coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
asgerf
left a comment
There was a problem hiding this comment.
CI checks are failing due newFeature not being a valid change note category
Co-authored-by: Asger F <asgerf@github.com>
i lost of track of this, thanks for being patient! new review requested |
|
Sorry another CI check is failing as |
The CWE-089/untyped/vercel.ts fixture added in this PR introduces a conn.query(...) call that DatabaseAccesses.ql reports, so its .expected baseline needs the corresponding entry. Output produced by `codeql test accept`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Vercel API handlers more often return JSON than HTML, so res.send is not the only response body sink that matters. Mirror Express's ResponseJsonCall by also matching res.json(...) and res.jsonp(...) on the response (direct and chained), and exercise the new behavior in the library-test fixture. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
nw! i regenerated DatabaseAccesses.expected. my bot also noticed that VercelNode.qll was missing sinks for json response types so added that too |
Adds a JavaScript framework model for
@vercel/nodeVercel serverless functions, so CodeQL's existing security queries can detect vulnerabilities in handlers ofthe form:
Since
@vercel/nodeis almost always imported asimport type, handlers are identified by their TypeScript parameter types (same approach asNextReqResHandlerinNext.qll). TheRouteHandlerclassadditionally requires the function to be a default export with
VercelRequest/VercelResponseat parameter positions 0 and 1, which excludes private helpers and misordered signatures.What the model teaches CodeQL
VercelNode::RouteHandlerVercelRequestat arg 0 andVercelResponseat arg 1Http::Servers::RequestSourcereqparameterHttp::Servers::ResponseSourceresparameter, plus chainedres.status()/res.type()/res.set()Http::RequestInputAccessreq.query(parameter),req.body(body),req.cookies(cookie),req.url(url, inherited fromIncomingMessage)Http::RequestHeaderAccessreq.headers.host,req.headers.referer, etc. — needed for host-header-poisoning and referer-XSS queriesHttp::ResponseSendArgumentres.send(...)andres.status(...).send(...)Http::RedirectInvocationres.redirect(...)Http::ExplicitHeaderDefinitionres.setHeader(name, value)Testing
Library test —
javascript/ql/test/library-tests/frameworks/vercel/Exercises every predicate and includes two negative cases:
internalHelper— same signature but not a default export → must not matchnotAHandler— default-exported withVercelRequest/VercelResponseat positions 1 and 2 (not 0 and 1) → must not matchQuery-consistency fixtures — one minimal handler per query, demonstrating end-to-end detection through the stock queries:
js/reflected-xssjs/request-forgeryjs/sql-injectionjs/command-line-injectionAll tests pass locally against CodeQL CLI 2.25.0.
Files
javascript/ql/lib/semmle/javascript/frameworks/VercelNode.qlljavascript/ql/lib/change-notes/2026-04-12-vercel-node.md(category: newFeature)javascript/ql/lib/javascript.qll(oneimportline)javascript/ql/test/library-tests/frameworks/vercel/(8 files)vercel.tsquery-test fixtures +.expectedupdatesdocs/codeql/reusables/supported-frameworks.rst(+1 row)Notes for reviewers
@vercel/nodehas no meaningful value-level API that handlers import, so a YAML data extension model wasn't viable. A minimal QL library following the established framework-model pattern is the right fit.req.urlas a source, (3) adding named-header access as anHttp::RequestHeaderAccess, and (4) enforcing that the typed parameters are at positions 0 and 1.