From 18b06f1cf465962d70915378e36188747c5f748c Mon Sep 17 00:00:00 2001 From: murderteeth <89237203+murderteeth@users.noreply.github.com> Date: Tue, 28 Apr 2026 16:14:53 +0000 Subject: [PATCH] Model res.json and res.jsonp as Vercel response sinks 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) --- .../javascript/frameworks/VercelNode.qll | 6 ++-- .../frameworks/vercel/src/vercel.ts | 5 +++ .../frameworks/vercel/tests.expected | 34 +++++++++++-------- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/VercelNode.qll b/javascript/ql/lib/semmle/javascript/frameworks/VercelNode.qll index ae206c9f915..9dcb25cf5db 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/VercelNode.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/VercelNode.qll @@ -141,8 +141,8 @@ module VercelNode { } /** - * An argument to `res.send(...)` on a Vercel response, including chained - * calls such as `res.status(200).send(...)`. + * An argument to `res.send(...)`, `res.json(...)`, or `res.jsonp(...)` on a + * Vercel response, including chained calls such as `res.status(200).json(...)`. */ private class ResponseSendArgument extends Http::ResponseSendArgument { RouteHandler rh; @@ -150,7 +150,7 @@ module VercelNode { ResponseSendArgument() { exists(Http::Servers::ResponseSource src | (src instanceof ResponseSource or src instanceof ChainedResponseSource) and - this = src.ref().getAMethodCall("send").getArgument(0) and + this = src.ref().getAMethodCall(["send", "json", "jsonp"]).getArgument(0) and rh = src.getRouteHandler() ) } diff --git a/javascript/ql/test/library-tests/frameworks/vercel/src/vercel.ts b/javascript/ql/test/library-tests/frameworks/vercel/src/vercel.ts index 0dae664e2c4..23956251ef4 100644 --- a/javascript/ql/test/library-tests/frameworks/vercel/src/vercel.ts +++ b/javascript/ql/test/library-tests/frameworks/vercel/src/vercel.ts @@ -22,6 +22,11 @@ export default function handler(req: VercelRequest, res: VercelResponse) { res.send(q); res.status(200).send(b); + // JSON response (direct and chained) + res.json(c); + res.status(200).json(u); + res.jsonp(host); + // Redirect res.redirect(req.query.url as string); } diff --git a/javascript/ql/test/library-tests/frameworks/vercel/tests.expected b/javascript/ql/test/library-tests/frameworks/vercel/tests.expected index a2929999f23..92d309cc02f 100644 --- a/javascript/ql/test/library-tests/frameworks/vercel/tests.expected +++ b/javascript/ql/test/library-tests/frameworks/vercel/tests.expected @@ -1,27 +1,31 @@ test_RouteHandler | src/now.ts:5:16:7:1 | functio ... ame);\\n} | -| src/vercel.ts:9:16:27:1 | functio ... ing);\\n} | +| src/vercel.ts:9:16:32:1 | functio ... ing);\\n} | test_RequestSource | src/now.ts:5:33:5:35 | req | src/now.ts:5:16:7:1 | functio ... ame);\\n} | -| src/vercel.ts:9:33:9:35 | req | src/vercel.ts:9:16:27:1 | functio ... ing);\\n} | +| src/vercel.ts:9:33:9:35 | req | src/vercel.ts:9:16:32:1 | functio ... ing);\\n} | test_ResponseSource | src/now.ts:5:50:5:52 | res | src/now.ts:5:16:7:1 | functio ... ame);\\n} | -| 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} | +| src/vercel.ts:9:53:9:55 | res | src/vercel.ts:9:16:32:1 | functio ... ing);\\n} | +| src/vercel.ts:23:3:23:17 | res.status(200) | src/vercel.ts:9:16:32:1 | functio ... ing);\\n} | +| src/vercel.ts:27:3:27:17 | res.status(200) | src/vercel.ts:9:16:32: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} | +| src/vercel.ts:19:3:19:44 | res.set ... /html") | content-type | src/vercel.ts:9:16:32: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} | +| src/vercel.ts:31:3:31:39 | res.red ... string) | src/vercel.ts:31:16:31:38 | req.que ... string | src/vercel.ts:9:16:32:1 | functio ... ing);\\n} | test_RequestInputAccess | src/now.ts:6:12:6:20 | req.query | parameter | src/now.ts:5:16:7:1 | functio ... ame);\\n} | -| 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} | +| src/vercel.ts:11:13:11:21 | req.query | parameter | src/vercel.ts:9:16:32:1 | functio ... ing);\\n} | +| src/vercel.ts:12:13:12:20 | req.body | body | src/vercel.ts:9:16:32:1 | functio ... ing);\\n} | +| src/vercel.ts:13:13:13:23 | req.cookies | cookie | src/vercel.ts:9:16:32:1 | functio ... ing);\\n} | +| src/vercel.ts:14:13:14:19 | req.url | url | src/vercel.ts:9:16:32:1 | functio ... ing);\\n} | +| src/vercel.ts:15:16:15:31 | req.headers.host | header | src/vercel.ts:9:16:32:1 | functio ... ing);\\n} | +| src/vercel.ts:16:15:16:33 | req.headers.referer | header | src/vercel.ts:9:16:32:1 | functio ... ing);\\n} | +| src/vercel.ts:31:16:31:24 | req.query | parameter | src/vercel.ts:9:16:32:1 | functio ... ing);\\n} | test_ResponseSendArgument | src/now.ts:6:12:6:25 | req.query.name | src/now.ts:5:16:7:1 | functio ... ame);\\n} | -| 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} | +| src/vercel.ts:22:12:22:12 | q | src/vercel.ts:9:16:32:1 | functio ... ing);\\n} | +| src/vercel.ts:23:24:23:24 | b | src/vercel.ts:9:16:32:1 | functio ... ing);\\n} | +| src/vercel.ts:26:12:26:12 | c | src/vercel.ts:9:16:32:1 | functio ... ing);\\n} | +| src/vercel.ts:27:24:27:24 | u | src/vercel.ts:9:16:32:1 | functio ... ing);\\n} | +| src/vercel.ts:28:13:28:16 | host | src/vercel.ts:9:16:32:1 | functio ... ing);\\n} |