mirror of
https://github.com/github/codeql.git
synced 2026-05-14 11:19:27 +02:00
JS: Add support for @vercel/node serverless functions
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>
This commit is contained in:
4
javascript/ql/lib/change-notes/2026-04-12-vercel-node.md
Normal file
4
javascript/ql/lib/change-notes/2026-04-12-vercel-node.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
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.
|
||||
@@ -134,6 +134,7 @@ import semmle.javascript.frameworks.TorrentLibraries
|
||||
import semmle.javascript.frameworks.Typeahead
|
||||
import semmle.javascript.frameworks.TrustedTypes
|
||||
import semmle.javascript.frameworks.UriLibraries
|
||||
import semmle.javascript.frameworks.VercelNode
|
||||
import semmle.javascript.frameworks.Vue
|
||||
import semmle.javascript.frameworks.Vuex
|
||||
import semmle.javascript.frameworks.Webix
|
||||
|
||||
200
javascript/ql/lib/semmle/javascript/frameworks/VercelNode.qll
Normal file
200
javascript/ql/lib/semmle/javascript/frameworks/VercelNode.qll
Normal file
@@ -0,0 +1,200 @@
|
||||
/**
|
||||
* Provides classes for working with [@vercel/node](https://www.npmjs.com/package/@vercel/node) Vercel serverless functions.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.frameworks.HTTP
|
||||
|
||||
/**
|
||||
* Provides classes for working with [@vercel/node](https://www.npmjs.com/package/@vercel/node) Vercel serverless functions.
|
||||
*
|
||||
* A Vercel serverless function is a module whose default export is a function
|
||||
* 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.
|
||||
*/
|
||||
module VercelNode {
|
||||
/**
|
||||
* A Vercel serverless function handler, identified as the default export of a
|
||||
* module whose first two parameters are typed as `VercelRequest` and
|
||||
* `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
|
||||
* constraint excludes private helpers or test utilities that share the
|
||||
* same signature.
|
||||
*/
|
||||
class RouteHandler extends Http::Servers::StandardRouteHandler, DataFlow::FunctionNode {
|
||||
DataFlow::ParameterNode req;
|
||||
DataFlow::ParameterNode res;
|
||||
|
||||
RouteHandler() {
|
||||
this = any(Module m).getAnExportedValue("default").getAFunctionValue() and
|
||||
req = this.getParameter(0) and
|
||||
res = this.getParameter(1) and
|
||||
req.hasUnderlyingType("@vercel/node", "VercelRequest") and
|
||||
res.hasUnderlyingType("@vercel/node", "VercelResponse")
|
||||
}
|
||||
|
||||
/** Gets the parameter that contains the request object. */
|
||||
DataFlow::ParameterNode getRequest() { result = req }
|
||||
|
||||
/** Gets the parameter that contains the response object. */
|
||||
DataFlow::ParameterNode getResponse() { result = res }
|
||||
}
|
||||
|
||||
/**
|
||||
* A Vercel request source, that is, the request parameter of a route handler.
|
||||
*/
|
||||
private class RequestSource extends Http::Servers::RequestSource {
|
||||
RouteHandler rh;
|
||||
|
||||
RequestSource() { this = rh.getRequest() }
|
||||
|
||||
override RouteHandler getRouteHandler() { result = rh }
|
||||
}
|
||||
|
||||
/**
|
||||
* A Vercel response source, that is, the response parameter of a route handler.
|
||||
*/
|
||||
private class ResponseSource extends Http::Servers::ResponseSource {
|
||||
RouteHandler rh;
|
||||
|
||||
ResponseSource() { this = rh.getResponse() }
|
||||
|
||||
override RouteHandler getRouteHandler() { result = rh }
|
||||
}
|
||||
|
||||
/**
|
||||
* A chained response, such as `res.status(200)`, `res.type('html')`, or `res.set(...)`.
|
||||
*
|
||||
* These methods return the response object and are commonly chained before `send` or `json`.
|
||||
*/
|
||||
private class ChainedResponseSource extends Http::Servers::ResponseSource {
|
||||
RouteHandler rh;
|
||||
|
||||
ChainedResponseSource() {
|
||||
exists(ResponseSource src |
|
||||
this = src.ref().getAMethodCall(["status", "type", "set"]) and
|
||||
rh = src.getRouteHandler()
|
||||
)
|
||||
}
|
||||
|
||||
override RouteHandler getRouteHandler() { result = rh }
|
||||
}
|
||||
|
||||
/**
|
||||
* An access to user-controlled input on a Vercel request.
|
||||
*
|
||||
* Covers `req.query`, `req.body`, `req.cookies`, and `req.url` (inherited
|
||||
* from Node's `IncomingMessage`). Named-header accesses like `req.headers.host`
|
||||
* are handled by `RequestHeaderAccess` below.
|
||||
*/
|
||||
private class RequestInputAccess extends Http::RequestInputAccess {
|
||||
RouteHandler rh;
|
||||
string kind;
|
||||
|
||||
RequestInputAccess() {
|
||||
exists(RequestSource src | rh = src.getRouteHandler() |
|
||||
this = src.ref().getAPropertyRead("query") and kind = "parameter"
|
||||
or
|
||||
this = src.ref().getAPropertyRead("body") and kind = "body"
|
||||
or
|
||||
this = src.ref().getAPropertyRead("cookies") and kind = "cookie"
|
||||
or
|
||||
this = src.ref().getAPropertyRead("url") and kind = "url"
|
||||
)
|
||||
or
|
||||
exists(RequestHeaderAccess access | this = access |
|
||||
rh = access.getRouteHandler() and
|
||||
kind = "header"
|
||||
)
|
||||
}
|
||||
|
||||
override RouteHandler getRouteHandler() { result = rh }
|
||||
|
||||
override string getKind() { result = kind }
|
||||
}
|
||||
|
||||
/**
|
||||
* An access to a named header on a Vercel request, for example
|
||||
* `req.headers.host` or `req.headers.referer`.
|
||||
*/
|
||||
private class RequestHeaderAccess extends Http::RequestHeaderAccess {
|
||||
RouteHandler rh;
|
||||
|
||||
RequestHeaderAccess() {
|
||||
exists(RequestSource src |
|
||||
this = src.ref().getAPropertyRead("headers").getAPropertyRead() and
|
||||
rh = src.getRouteHandler()
|
||||
)
|
||||
}
|
||||
|
||||
override string getAHeaderName() {
|
||||
result = this.(DataFlow::PropRead).getPropertyName().toLowerCase()
|
||||
}
|
||||
|
||||
override RouteHandler getRouteHandler() { result = rh }
|
||||
|
||||
override string getKind() { result = "header" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument to `res.send(...)` on a Vercel response, including chained
|
||||
* calls such as `res.status(200).send(...)`.
|
||||
*/
|
||||
private class ResponseSendArgument extends Http::ResponseSendArgument {
|
||||
RouteHandler rh;
|
||||
|
||||
ResponseSendArgument() {
|
||||
exists(Http::Servers::ResponseSource src |
|
||||
(src instanceof ResponseSource or src instanceof ChainedResponseSource) and
|
||||
this = src.ref().getAMethodCall("send").getArgument(0) and
|
||||
rh = src.getRouteHandler()
|
||||
)
|
||||
}
|
||||
|
||||
override RouteHandler getRouteHandler() { result = rh }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `res.redirect(...)` on a Vercel response.
|
||||
*/
|
||||
private class RedirectInvocation extends Http::RedirectInvocation, DataFlow::MethodCallNode {
|
||||
RouteHandler rh;
|
||||
|
||||
RedirectInvocation() {
|
||||
exists(ResponseSource src |
|
||||
this = src.ref().getAMethodCall("redirect") and
|
||||
rh = src.getRouteHandler()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getUrlArgument() { result = this.getLastArgument() }
|
||||
|
||||
override RouteHandler getRouteHandler() { result = rh }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `res.setHeader(name, value)` on a Vercel response.
|
||||
*/
|
||||
private class SetHeader extends Http::ExplicitHeaderDefinition, DataFlow::CallNode {
|
||||
RouteHandler rh;
|
||||
|
||||
SetHeader() {
|
||||
exists(ResponseSource src |
|
||||
this = src.ref().getAMethodCall("setHeader") and
|
||||
rh = src.getRouteHandler()
|
||||
)
|
||||
}
|
||||
|
||||
override RouteHandler getRouteHandler() { result = rh }
|
||||
|
||||
override predicate definesHeaderValue(string headerName, DataFlow::Node headerValue) {
|
||||
headerName = this.getArgument(0).getStringValue().toLowerCase() and
|
||||
headerValue = this.getArgument(1)
|
||||
}
|
||||
|
||||
override DataFlow::Node getNameNode() { result = this.getArgument(0) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_HeaderDefinition(
|
||||
Http::HeaderDefinition hd, string name, VercelNode::RouteHandler rh
|
||||
) {
|
||||
hd.getRouteHandler() = rh and name = hd.getAHeaderName()
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_RedirectInvocation(
|
||||
Http::RedirectInvocation call, DataFlow::Node url, VercelNode::RouteHandler rh
|
||||
) {
|
||||
call.getRouteHandler() = rh and url = call.getUrlArgument()
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_RequestInputAccess(
|
||||
Http::RequestInputAccess ria, string kind, VercelNode::RouteHandler rh
|
||||
) {
|
||||
ria.getRouteHandler() = rh and kind = ria.getKind()
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_RequestSource(Http::Servers::RequestSource src, VercelNode::RouteHandler rh) {
|
||||
src.getRouteHandler() = rh
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_ResponseSendArgument(
|
||||
Http::ResponseSendArgument arg, VercelNode::RouteHandler rh
|
||||
) {
|
||||
arg.getRouteHandler() = rh
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_ResponseSource(Http::Servers::ResponseSource src, VercelNode::RouteHandler rh) {
|
||||
src.getRouteHandler() = rh
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_RouteHandler(VercelNode::RouteHandler rh) { any() }
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { VercelRequest, VercelResponse } from "@vercel/node";
|
||||
|
||||
// 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.
|
||||
export default function notAHandler(ctx: unknown, req: VercelRequest, res: VercelResponse) {
|
||||
res.send(req.query.name);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { VercelRequest, VercelResponse } from "@vercel/node";
|
||||
|
||||
// A private helper with the same signature. Must NOT be recognised as a
|
||||
// route handler, since Vercel only invokes the default export.
|
||||
function internalHelper(req: VercelRequest, res: VercelResponse) {
|
||||
res.send(req.query.name);
|
||||
}
|
||||
|
||||
export default function handler(req: VercelRequest, res: VercelResponse) {
|
||||
// Request inputs
|
||||
const q = req.query; // source: parameter
|
||||
const b = req.body; // source: body
|
||||
const c = req.cookies; // source: cookie
|
||||
const u = req.url; // source: url (inherited from IncomingMessage)
|
||||
const host = req.headers.host; // source: header (named)
|
||||
const ref = req.headers.referer; // source: header (named)
|
||||
|
||||
// Response header definition
|
||||
res.setHeader("Content-Type", "text/html");
|
||||
|
||||
// Response send (direct and chained)
|
||||
res.send(q);
|
||||
res.status(200).send(b);
|
||||
|
||||
// Redirect
|
||||
res.redirect(req.query.url as string);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
test_RouteHandler
|
||||
| src/vercel.ts:9:16:27:1 | functio ... ing);\\n} |
|
||||
test_RequestSource
|
||||
| src/vercel.ts:9:33:9:35 | req | src/vercel.ts:9:16:27:1 | functio ... ing);\\n} |
|
||||
test_ResponseSource
|
||||
| src/vercel.ts:9:53:9:55 | res | src/vercel.ts:9:16:27:1 | functio ... ing);\\n} |
|
||||
| src/vercel.ts:23:3:23:17 | res.status(200) | src/vercel.ts:9:16:27:1 | functio ... ing);\\n} |
|
||||
test_HeaderDefinition
|
||||
| src/vercel.ts:19:3:19:44 | res.set ... /html") | content-type | src/vercel.ts:9:16:27:1 | functio ... ing);\\n} |
|
||||
test_RedirectInvocation
|
||||
| src/vercel.ts:26:3:26:39 | res.red ... string) | src/vercel.ts:26:16:26:38 | req.que ... string | src/vercel.ts:9:16:27:1 | functio ... ing);\\n} |
|
||||
test_RequestInputAccess
|
||||
| src/vercel.ts:11:13:11:21 | req.query | parameter | src/vercel.ts:9:16:27:1 | functio ... ing);\\n} |
|
||||
| src/vercel.ts:12:13:12:20 | req.body | body | src/vercel.ts:9:16:27:1 | functio ... ing);\\n} |
|
||||
| src/vercel.ts:13:13:13:23 | req.cookies | cookie | src/vercel.ts:9:16:27:1 | functio ... ing);\\n} |
|
||||
| src/vercel.ts:14:13:14:19 | req.url | url | src/vercel.ts:9:16:27:1 | functio ... ing);\\n} |
|
||||
| src/vercel.ts:15:16:15:31 | req.headers.host | header | src/vercel.ts:9:16:27:1 | functio ... ing);\\n} |
|
||||
| src/vercel.ts:16:15:16:33 | req.headers.referer | header | src/vercel.ts:9:16:27:1 | functio ... ing);\\n} |
|
||||
| src/vercel.ts:26:16:26:24 | req.query | parameter | src/vercel.ts:9:16:27:1 | functio ... ing);\\n} |
|
||||
test_ResponseSendArgument
|
||||
| src/vercel.ts:22:12:22:12 | q | src/vercel.ts:9:16:27:1 | functio ... ing);\\n} |
|
||||
| src/vercel.ts:23:24:23:24 | b | src/vercel.ts:9:16:27:1 | functio ... ing);\\n} |
|
||||
@@ -0,0 +1,7 @@
|
||||
import RouteHandler
|
||||
import RequestSource
|
||||
import ResponseSource
|
||||
import RequestInputAccess
|
||||
import HeaderDefinition
|
||||
import ResponseSendArgument
|
||||
import RedirectInvocation
|
||||
@@ -110,6 +110,8 @@
|
||||
| promisification.js:151:28:151:31 | code | promisification.js:141:18:141:25 | req.body | promisification.js:151:28:151:31 | code | This command line depends on a $@. | promisification.js:141:18:141:25 | req.body | user-provided value |
|
||||
| promisification.js:152:25:152:28 | code | promisification.js:141:18:141:25 | req.body | promisification.js:152:25:152:28 | code | This command line depends on a $@. | promisification.js:141:18:141:25 | req.body | user-provided value |
|
||||
| third-party-command-injection.js:6:21:6:27 | command | third-party-command-injection.js:5:20:5:26 | command | third-party-command-injection.js:6:21:6:27 | command | This command line depends on a $@. | third-party-command-injection.js:5:20:5:26 | command | user-provided value |
|
||||
| vercel.ts:6:8:6:21 | "echo " + name | vercel.ts:5:16:5:24 | req.query | vercel.ts:6:8:6:21 | "echo " + name | This command line depends on a $@. | vercel.ts:5:16:5:24 | req.query | user-provided value |
|
||||
| vercel.ts:6:8:6:21 | "echo " + name | vercel.ts:5:16:5:29 | req.query.name | vercel.ts:6:8:6:21 | "echo " + name | This command line depends on a $@. | vercel.ts:5:16:5:29 | req.query.name | user-provided value |
|
||||
edges
|
||||
| actions.js:8:9:8:13 | title | actions.js:9:16:9:20 | title | provenance | |
|
||||
| actions.js:8:17:8:57 | github. ... t.title | actions.js:8:9:8:13 | title | provenance | |
|
||||
@@ -340,6 +342,10 @@ edges
|
||||
| promisification.js:141:11:141:14 | code | promisification.js:152:25:152:28 | code | provenance | |
|
||||
| promisification.js:141:18:141:25 | req.body | promisification.js:141:11:141:14 | code | provenance | |
|
||||
| third-party-command-injection.js:5:20:5:26 | command | third-party-command-injection.js:6:21:6:27 | command | provenance | |
|
||||
| vercel.ts:5:9:5:12 | name | vercel.ts:6:18:6:21 | name | provenance | |
|
||||
| vercel.ts:5:16:5:24 | req.query | vercel.ts:5:9:5:12 | name | provenance | |
|
||||
| vercel.ts:5:16:5:29 | req.query.name | vercel.ts:5:9:5:12 | name | provenance | |
|
||||
| vercel.ts:6:18:6:21 | name | vercel.ts:6:8:6:21 | "echo " + name | provenance | |
|
||||
nodes
|
||||
| actions.js:8:9:8:13 | title | semmle.label | title |
|
||||
| actions.js:8:17:8:57 | github. ... t.title | semmle.label | github. ... t.title |
|
||||
@@ -591,6 +597,11 @@ nodes
|
||||
| promisification.js:152:25:152:28 | code | semmle.label | code |
|
||||
| third-party-command-injection.js:5:20:5:26 | command | semmle.label | command |
|
||||
| third-party-command-injection.js:6:21:6:27 | command | semmle.label | command |
|
||||
| vercel.ts:5:9:5:12 | name | semmle.label | name |
|
||||
| vercel.ts:5:16:5:24 | req.query | semmle.label | req.query |
|
||||
| vercel.ts:5:16:5:29 | req.query.name | semmle.label | req.query.name |
|
||||
| vercel.ts:6:8:6:21 | "echo " + name | semmle.label | "echo " + name |
|
||||
| vercel.ts:6:18:6:21 | name | semmle.label | name |
|
||||
subpaths
|
||||
| promisification.js:116:32:116:34 | cmd | promisification.js:118:21:118:23 | cmd | promisification.js:117:29:117:35 | resolve [Return] [resolve-value] | promisification.js:117:16:119:10 | new Pro ... }) [PromiseValue] |
|
||||
| promisification.js:122:42:122:45 | code | promisification.js:116:32:116:34 | cmd | promisification.js:117:16:119:10 | new Pro ... }) [PromiseValue] | promisification.js:122:24:122:46 | createE ... e(code) [PromiseValue] |
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { VercelRequest, VercelResponse } from "@vercel/node";
|
||||
import { exec } from "child_process";
|
||||
|
||||
export default function handler(req: VercelRequest, res: VercelResponse) {
|
||||
const name = req.query.name as string; // $ Source
|
||||
exec("echo " + name, (err, stdout) => { // $ Alert
|
||||
res.send(stdout);
|
||||
});
|
||||
}
|
||||
@@ -74,6 +74,8 @@
|
||||
| tst2.js:113:12:113:17 | unsafe | tst2.js:105:9:105:9 | p | tst2.js:113:12:113:17 | unsafe | Cross-site scripting vulnerability due to a $@. | tst2.js:105:9:105:9 | p | user-provided value |
|
||||
| tst3.js:6:12:6:12 | p | tst3.js:5:9:5:9 | p | tst3.js:6:12:6:12 | p | Cross-site scripting vulnerability due to a $@. | tst3.js:5:9:5:9 | p | user-provided value |
|
||||
| tst3.js:12:12:12:15 | code | tst3.js:11:32:11:39 | reg.body | tst3.js:12:12:12:15 | code | Cross-site scripting vulnerability due to a $@. | tst3.js:11:32:11:39 | reg.body | user-provided value |
|
||||
| vercel.ts:5:24:5:51 | `<h1>${ ... }</h1>` | vercel.ts:5:31:5:39 | req.query | vercel.ts:5:24:5:51 | `<h1>${ ... }</h1>` | Cross-site scripting vulnerability due to a $@. | vercel.ts:5:31:5:39 | req.query | user-provided value |
|
||||
| vercel.ts:5:24:5:51 | `<h1>${ ... }</h1>` | vercel.ts:5:31:5:44 | req.query.name | vercel.ts:5:24:5:51 | `<h1>${ ... }</h1>` | Cross-site scripting vulnerability due to a $@. | vercel.ts:5:31:5:44 | req.query.name | user-provided value |
|
||||
edges
|
||||
| ReflectedXss.js:7:33:7:45 | req.params.id | ReflectedXss.js:7:14:7:45 | "Unknow ... rams.id | provenance | |
|
||||
| ReflectedXss.js:16:31:16:39 | params.id | ReflectedXss.js:16:12:16:39 | "Unknow ... rams.id | provenance | |
|
||||
@@ -259,6 +261,8 @@ edges
|
||||
| tst3.js:11:9:11:12 | code | tst3.js:12:12:12:15 | code | provenance | |
|
||||
| tst3.js:11:16:11:74 | prettie ... bel" }) | tst3.js:11:9:11:12 | code | provenance | |
|
||||
| tst3.js:11:32:11:39 | reg.body | tst3.js:11:16:11:74 | prettie ... bel" }) | provenance | |
|
||||
| vercel.ts:5:31:5:39 | req.query | vercel.ts:5:24:5:51 | `<h1>${ ... }</h1>` | provenance | |
|
||||
| vercel.ts:5:31:5:44 | req.query.name | vercel.ts:5:24:5:51 | `<h1>${ ... }</h1>` | provenance | |
|
||||
nodes
|
||||
| ReflectedXss.js:7:14:7:45 | "Unknow ... rams.id | semmle.label | "Unknow ... rams.id |
|
||||
| ReflectedXss.js:7:33:7:45 | req.params.id | semmle.label | req.params.id |
|
||||
@@ -497,5 +501,8 @@ nodes
|
||||
| tst3.js:11:16:11:74 | prettie ... bel" }) | semmle.label | prettie ... bel" }) |
|
||||
| tst3.js:11:32:11:39 | reg.body | semmle.label | reg.body |
|
||||
| tst3.js:12:12:12:15 | code | semmle.label | code |
|
||||
| vercel.ts:5:24:5:51 | `<h1>${ ... }</h1>` | semmle.label | `<h1>${ ... }</h1>` |
|
||||
| vercel.ts:5:31:5:39 | req.query | semmle.label | req.query |
|
||||
| vercel.ts:5:31:5:44 | req.query.name | semmle.label | req.query.name |
|
||||
subpaths
|
||||
| ReflectedXssGood3.js:139:24:139:26 | url | ReflectedXssGood3.js:68:22:68:26 | value | ReflectedXssGood3.js:108:10:108:23 | parts.join('') | ReflectedXssGood3.js:139:12:139:27 | escapeHtml3(url) |
|
||||
|
||||
@@ -72,3 +72,5 @@
|
||||
| tst2.js:113:12:113:17 | unsafe | Cross-site scripting vulnerability due to $@. | tst2.js:105:9:105:9 | p | user-provided value |
|
||||
| tst3.js:6:12:6:12 | p | Cross-site scripting vulnerability due to $@. | tst3.js:5:9:5:9 | p | user-provided value |
|
||||
| tst3.js:12:12:12:15 | code | Cross-site scripting vulnerability due to $@. | tst3.js:11:32:11:39 | reg.body | user-provided value |
|
||||
| vercel.ts:5:24:5:51 | `<h1>${ ... }</h1>` | Cross-site scripting vulnerability due to $@. | vercel.ts:5:31:5:39 | req.query | user-provided value |
|
||||
| vercel.ts:5:24:5:51 | `<h1>${ ... }</h1>` | Cross-site scripting vulnerability due to $@. | vercel.ts:5:31:5:44 | req.query.name | user-provided value |
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { VercelRequest, VercelResponse } from "@vercel/node";
|
||||
|
||||
export default function handler(req: VercelRequest, res: VercelResponse) {
|
||||
res.setHeader("Content-Type", "text/html");
|
||||
res.status(200).send(`<h1>${req.query.name}</h1>`); // $ Alert
|
||||
}
|
||||
@@ -156,6 +156,8 @@
|
||||
| tst3.js:9:14:9:19 | query1 | tst3.js:8:16:8:34 | req.params.category | tst3.js:9:14:9:19 | query1 | This query string depends on a $@. | tst3.js:8:16:8:34 | req.params.category | user-provided value |
|
||||
| tst4.js:8:10:8:66 | 'SELECT ... d + '"' | tst4.js:8:46:8:60 | $routeParams.id | tst4.js:8:10:8:66 | 'SELECT ... d + '"' | This query string depends on a $@. | tst4.js:8:46:8:60 | $routeParams.id | user-provided value |
|
||||
| tst.js:10:10:10:64 | 'SELECT ... d + '"' | tst.js:10:46:10:58 | req.params.id | tst.js:10:10:10:64 | 'SELECT ... d + '"' | This query string depends on a $@. | tst.js:10:46:10:58 | req.params.id | user-provided value |
|
||||
| vercel.ts:7:14:7:51 | "SELECT ... " + id | vercel.ts:6:14:6:22 | req.query | vercel.ts:7:14:7:51 | "SELECT ... " + id | This query string depends on a $@. | vercel.ts:6:14:6:22 | req.query | user-provided value |
|
||||
| vercel.ts:7:14:7:51 | "SELECT ... " + id | vercel.ts:6:14:6:25 | req.query.id | vercel.ts:7:14:7:51 | "SELECT ... " + id | This query string depends on a $@. | vercel.ts:6:14:6:25 | req.query.id | user-provided value |
|
||||
edges
|
||||
| athena.js:9:11:9:19 | userQuery | athena.js:14:30:14:38 | userQuery | provenance | |
|
||||
| athena.js:9:11:9:19 | userQuery | athena.js:24:22:24:30 | userQuery | provenance | |
|
||||
@@ -620,6 +622,10 @@ edges
|
||||
| tst3.js:8:16:8:34 | req.params.category | tst3.js:7:7:7:12 | query1 | provenance | |
|
||||
| tst4.js:8:46:8:60 | $routeParams.id | tst4.js:8:10:8:66 | 'SELECT ... d + '"' | provenance | |
|
||||
| tst.js:10:46:10:58 | req.params.id | tst.js:10:10:10:64 | 'SELECT ... d + '"' | provenance | |
|
||||
| vercel.ts:6:9:6:10 | id | vercel.ts:7:50:7:51 | id | provenance | |
|
||||
| vercel.ts:6:14:6:22 | req.query | vercel.ts:6:9:6:10 | id | provenance | |
|
||||
| vercel.ts:6:14:6:25 | req.query.id | vercel.ts:6:9:6:10 | id | provenance | |
|
||||
| vercel.ts:7:50:7:51 | id | vercel.ts:7:14:7:51 | "SELECT ... " + id | provenance | |
|
||||
nodes
|
||||
| athena.js:9:11:9:19 | userQuery | semmle.label | userQuery |
|
||||
| athena.js:9:23:9:30 | req.body | semmle.label | req.body |
|
||||
@@ -1029,4 +1035,9 @@ nodes
|
||||
| tst4.js:8:46:8:60 | $routeParams.id | semmle.label | $routeParams.id |
|
||||
| tst.js:10:10:10:64 | 'SELECT ... d + '"' | semmle.label | 'SELECT ... d + '"' |
|
||||
| tst.js:10:46:10:58 | req.params.id | semmle.label | req.params.id |
|
||||
| vercel.ts:6:9:6:10 | id | semmle.label | id |
|
||||
| vercel.ts:6:14:6:22 | req.query | semmle.label | req.query |
|
||||
| vercel.ts:6:14:6:25 | req.query.id | semmle.label | req.query.id |
|
||||
| vercel.ts:7:14:7:51 | "SELECT ... " + id | semmle.label | "SELECT ... " + id |
|
||||
| vercel.ts:7:50:7:51 | id | semmle.label | id |
|
||||
subpaths
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import type { VercelRequest, VercelResponse } from "@vercel/node";
|
||||
const mysql = require("mysql");
|
||||
const conn = mysql.createConnection({});
|
||||
|
||||
export default function handler(req: VercelRequest, res: VercelResponse) {
|
||||
const id = req.query.id as string; // $ Source
|
||||
conn.query("SELECT * FROM users WHERE id = " + id, (err: any, rows: any) => { // $ Alert
|
||||
res.json(rows);
|
||||
});
|
||||
}
|
||||
@@ -40,6 +40,8 @@
|
||||
| serverSide.js:145:5:145:25 | axios.g ... dedUrl) | serverSide.js:139:19:139:31 | req.query.url | serverSide.js:145:15:145:24 | encodedUrl | The $@ of this request depends on a $@. | serverSide.js:145:15:145:24 | encodedUrl | URL | serverSide.js:139:19:139:31 | req.query.url | user-provided value |
|
||||
| serverSide.js:147:5:147:25 | axios.g ... pedUrl) | serverSide.js:139:19:139:31 | req.query.url | serverSide.js:147:15:147:24 | escapedUrl | The $@ of this request depends on a $@. | serverSide.js:147:15:147:24 | escapedUrl | URL | serverSide.js:139:19:139:31 | req.query.url | user-provided value |
|
||||
| serverSide.js:151:1:151:15 | request(custom) | serverSide.js:150:16:150:51 | require ... ource() | serverSide.js:151:9:151:14 | custom | The $@ of this request depends on a $@. | serverSide.js:151:9:151:14 | custom | URL | serverSide.js:150:16:150:51 | require ... ource() | user-provided value |
|
||||
| vercel.ts:5:26:5:35 | fetch(url) | vercel.ts:4:15:4:23 | req.query | vercel.ts:5:32:5:34 | url | The $@ of this request depends on a $@. | vercel.ts:5:32:5:34 | url | URL | vercel.ts:4:15:4:23 | req.query | user-provided value |
|
||||
| vercel.ts:5:26:5:35 | fetch(url) | vercel.ts:4:15:4:27 | req.query.url | vercel.ts:5:32:5:34 | url | The $@ of this request depends on a $@. | vercel.ts:5:32:5:34 | url | URL | vercel.ts:4:15:4:27 | req.query.url | user-provided value |
|
||||
edges
|
||||
| Request/app/api/proxy/route2.serverSide.ts:4:9:4:15 | { url } | Request/app/api/proxy/route2.serverSide.ts:4:11:4:13 | url | provenance | |
|
||||
| Request/app/api/proxy/route2.serverSide.ts:4:11:4:13 | url | Request/app/api/proxy/route2.serverSide.ts:5:27:5:29 | url | provenance | |
|
||||
@@ -147,6 +149,9 @@ edges
|
||||
| serverSide.js:146:31:146:35 | input | serverSide.js:146:24:146:36 | escape(input) | provenance | |
|
||||
| serverSide.js:150:7:150:12 | custom | serverSide.js:151:9:151:14 | custom | provenance | |
|
||||
| serverSide.js:150:16:150:51 | require ... ource() | serverSide.js:150:7:150:12 | custom | provenance | |
|
||||
| vercel.ts:4:9:4:11 | url | vercel.ts:5:32:5:34 | url | provenance | |
|
||||
| vercel.ts:4:15:4:23 | req.query | vercel.ts:4:9:4:11 | url | provenance | |
|
||||
| vercel.ts:4:15:4:27 | req.query.url | vercel.ts:4:9:4:11 | url | provenance | |
|
||||
nodes
|
||||
| Request/app/api/proxy/route2.serverSide.ts:4:9:4:15 | { url } | semmle.label | { url } |
|
||||
| Request/app/api/proxy/route2.serverSide.ts:4:11:4:13 | url | semmle.label | url |
|
||||
@@ -277,4 +282,8 @@ nodes
|
||||
| serverSide.js:150:7:150:12 | custom | semmle.label | custom |
|
||||
| serverSide.js:150:16:150:51 | require ... ource() | semmle.label | require ... ource() |
|
||||
| serverSide.js:151:9:151:14 | custom | semmle.label | custom |
|
||||
| vercel.ts:4:9:4:11 | url | semmle.label | url |
|
||||
| vercel.ts:4:15:4:23 | req.query | semmle.label | req.query |
|
||||
| vercel.ts:4:15:4:27 | req.query.url | semmle.label | req.query.url |
|
||||
| vercel.ts:5:32:5:34 | url | semmle.label | url |
|
||||
subpaths
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { VercelRequest, VercelResponse } from "@vercel/node";
|
||||
|
||||
export default async function handler(req: VercelRequest, res: VercelResponse) {
|
||||
const url = req.query.url as string; // $ Source[js/request-forgery]
|
||||
const response = await fetch(url); // $ Alert[js/request-forgery]
|
||||
res.json(await response.json());
|
||||
}
|
||||
Reference in New Issue
Block a user