Skip to content

JS: Add support for @vercel/node serverless functions#21697

Open
murderteeth wants to merge 1 commit intogithub:mainfrom
yearn:js/vercel-node-framework
Open

JS: Add support for @vercel/node serverless functions#21697
murderteeth wants to merge 1 commit intogithub:mainfrom
yearn:js/vercel-node-framework

Conversation

@murderteeth
Copy link
Copy Markdown

Adds a JavaScript framework model for @vercel/node Vercel serverless functions, so CodeQL's existing security queries can detect vulnerabilities in handlers of
the form:

export default function handler(req: VercelRequest, res: VercelResponse) { ... }

Since @vercel/node is almost always imported as import type, handlers are identified by their TypeScript parameter types (same approach as NextReqResHandler in Next.qll). The RouteHandler class
additionally requires the function to be a default export with VercelRequest/VercelResponse at parameter positions 0 and 1, which excludes private helpers and misordered signatures.

What the model teaches CodeQL

Concept Class Covers
Route handler VercelNode::RouteHandler Default-exported function with VercelRequest at arg 0 and VercelResponse at arg 1
Request source Http::Servers::RequestSource The req parameter
Response source Http::Servers::ResponseSource The res parameter, plus chained res.status() / res.type() / res.set()
Request inputs Http::RequestInputAccess req.query (parameter), req.body (body), req.cookies (cookie), req.url (url, inherited from IncomingMessage)
Named headers Http::RequestHeaderAccess req.headers.host, req.headers.referer, etc. — needed for host-header-poisoning and referer-XSS queries
Response sink Http::ResponseSendArgument Argument of res.send(...) and res.status(...).send(...)
Redirect Http::RedirectInvocation res.redirect(...)
Response header Http::ExplicitHeaderDefinition res.setHeader(name, value)

Testing

Library testjavascript/ql/test/library-tests/frameworks/vercel/
Exercises every predicate and includes two negative cases:

  • internalHelper — same signature but not a default export → must not match
  • notAHandler — default-exported with VercelRequest/VercelResponse at positions 1 and 2 (not 0 and 1) → must not match

Query-consistency fixtures — one minimal handler per query, demonstrating end-to-end detection through the stock queries:

  • js/reflected-xss
  • js/request-forgery
  • js/sql-injection
  • js/command-line-injection

All tests pass locally against CodeQL CLI 2.25.0.

Files

  • New: javascript/ql/lib/semmle/javascript/frameworks/VercelNode.qll
  • New: javascript/ql/lib/change-notes/2026-04-12-vercel-node.md (category: newFeature)
  • Modified: javascript/ql/lib/javascript.qll (one import line)
  • New: javascript/ql/test/library-tests/frameworks/vercel/ (8 files)
  • New: 4 × vercel.ts query-test fixtures + .expected updates
  • Modified: docs/codeql/reusables/supported-frameworks.rst (+1 row)

Notes for reviewers

  • @vercel/node has 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.
  • Pre-submission review raised four concerns which are all addressed in this PR: (1) excluding private helpers that share the signature, (2) adding req.url as a source, (3) adding named-header access as an
    Http::RequestHeaderAccess, and (4) enforcing that the typed parameters are at positions 0 and 1.

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>
Copilot AI review requested due to automatic review settings April 12, 2026 19:53
@murderteeth murderteeth requested review from a team as code owners April 12, 2026 19:53
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 VercelNode framework modeling library and wires it into javascript.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
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Suggested change
* are recognised by their TypeScript parameter types. The default-export
* are recognized by their TypeScript parameter types. The default-export

Copilot uses AI. Check for mistakes.
Comment on lines +12 to +14
* 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.
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
* 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.

Copilot uses AI. Check for mistakes.
---
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.
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spelling: change “recognised” to “recognized” for consistency with other JavaScript change notes (which use US spelling).

Suggested change
* 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.

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,27 @@
import type { VercelRequest, VercelResponse } from "@vercel/node";

// A private helper with the same signature. Must NOT be recognised as a
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spelling in comment: change “recognised” to “recognized” to match the rest of the JavaScript codebase’s comment spelling.

Suggested change
// 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

Copilot uses AI. Check for mistakes.

// 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.
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spelling in comment: change “recognised” to “recognized” to match the rest of the JavaScript codebase’s comment spelling.

Suggested change
// so it must NOT be recognised as a route handler.
// so it must NOT be recognized as a route handler.

Copilot uses AI. Check for mistakes.
superagent, Network communicator
swig, templating language
underscore, Utility library
vercel, Serverless framework
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
vercel, Serverless framework
Vercel (@vercel/node), Serverless framework

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@asgerf asgerf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Simple and clean. I have one potential improvement, otherwise looks good.

Comment on lines +35 to +36
req.hasUnderlyingType("@vercel/node", "VercelRequest") and
res.hasUnderlyingType("@vercel/node", "VercelResponse")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
req.hasUnderlyingType("@vercel/node", "VercelRequest") and
res.hasUnderlyingType("@vercel/node", "VercelResponse")
req.hasUnderlyingType(["@vercel/node", "@now/node"], ["NowRequest", "VercelRequest"]) and
res.hasUnderlyingType(["@vercel/node", "@now/node"], ["NowResponse", "VercelResponse"])

After testing this on some code in the wild, it seems there are some deprecated aliases for these types worth covering here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants