diff --git a/javascript/ql/lib/change-notes/2025-04-14-fastify-addhook.md b/javascript/ql/lib/change-notes/2025-04-14-fastify-addhook.md new file mode 100644 index 000000000000..a9e754bd56ea --- /dev/null +++ b/javascript/ql/lib/change-notes/2025-04-14-fastify-addhook.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added support for the `fastify` `addHook` method. diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Fastify.qll b/javascript/ql/lib/semmle/javascript/frameworks/Fastify.qll index 2b8d6287d78d..53c92e7f7b33 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Fastify.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Fastify.qll @@ -138,7 +138,7 @@ module Fastify { RouteSetup() { this = server(server).getAMethodCall(methodName) and - methodName = ["route", "get", "head", "post", "put", "delete", "options", "patch"] + methodName = ["route", "get", "head", "post", "put", "delete", "options", "patch", "addHook"] } override DataFlow::SourceNode getARouteHandler() { @@ -238,6 +238,20 @@ module Fastify { } } + /** + * Gets the property name where user-controlled input is written to a request or response object + * in a route handler. This is used to track taint flow through request and response object properties. + */ + private string getUserControlledPropertyName() { + exists(DataFlow::PropWrite write, DataFlow::Node source, RouteHandler rh | + write.getBase*() = + [rh.getARequestSource().ref().getALocalUse(), rh.getAResponseSource().ref().getALocalUse()] and + write.getPropertyName() = result and + write.getRhs() = source and + source = any(Http::RequestInputAccess ria).getASuccessor*() + ) + } + /** * An access to a user-controlled Fastify request input. */ @@ -252,6 +266,20 @@ module Fastify { or kind = "body" and name = "body" + or + kind = "stored" and + name = getUserControlledPropertyName() + ) + or + // Handle reading from reply object with user input stored on it + exists(string name | + ( + this = rh.getAResponseSource().ref().getAPropertyRead(name) + or + this = rh.getAResponseSource().ref().getAPropertyRead+().getAPropertyRead(name) + ) and + kind = "stored" and + name = getUserControlledPropertyName() ) } diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected index a81b9dbcce0f..9a81aa80a2c0 100644 --- a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected @@ -27,6 +27,33 @@ | express.js:20:34:20:38 | taint | express.js:19:17:19:35 | req.param("wobble") | express.js:20:34:20:38 | taint | This code execution depends on a $@. | express.js:19:17:19:35 | req.param("wobble") | user-provided value | | express.js:36:15:36:19 | taint | express.js:27:17:27:35 | req.param("wobble") | express.js:36:15:36:19 | taint | This code execution depends on a $@. | express.js:27:17:27:35 | req.param("wobble") | user-provided value | | express.js:43:10:43:12 | msg | express.js:42:30:42:32 | msg | express.js:43:10:43:12 | msg | This code execution depends on a $@. | express.js:42:30:42:32 | msg | user-provided value | +| fastify.js:5:44:5:52 | userInput | fastify.js:4:21:4:33 | request.query | fastify.js:5:44:5:52 | userInput | This code execution depends on a $@. | fastify.js:4:21:4:33 | request.query | user-provided value | +| fastify.js:5:44:5:52 | userInput | fastify.js:4:21:4:43 | request ... Request | fastify.js:5:44:5:52 | userInput | This code execution depends on a $@. | fastify.js:4:21:4:43 | request ... Request | user-provided value | +| fastify.js:10:44:10:52 | userInput | fastify.js:9:21:9:33 | request.query | fastify.js:10:44:10:52 | userInput | This code execution depends on a $@. | fastify.js:9:21:9:33 | request.query | user-provided value | +| fastify.js:10:44:10:52 | userInput | fastify.js:9:21:9:40 | request.query.onSend | fastify.js:10:44:10:52 | userInput | This code execution depends on a $@. | fastify.js:9:21:9:40 | request.query.onSend | user-provided value | +| fastify.js:16:44:16:52 | userInput | fastify.js:15:21:15:33 | request.query | fastify.js:16:44:16:52 | userInput | This code execution depends on a $@. | fastify.js:15:21:15:33 | request.query | user-provided value | +| fastify.js:16:44:16:52 | userInput | fastify.js:15:21:15:44 | request ... Parsing | fastify.js:16:44:16:52 | userInput | This code execution depends on a $@. | fastify.js:15:21:15:44 | request ... Parsing | user-provided value | +| fastify.js:22:44:22:52 | userInput | fastify.js:21:21:21:33 | request.query | fastify.js:22:44:22:52 | userInput | This code execution depends on a $@. | fastify.js:21:21:21:33 | request.query | user-provided value | +| fastify.js:22:44:22:52 | userInput | fastify.js:21:21:21:47 | request ... idation | fastify.js:22:44:22:52 | userInput | This code execution depends on a $@. | fastify.js:21:21:21:47 | request ... idation | user-provided value | +| fastify.js:27:44:27:52 | userInput | fastify.js:26:21:26:33 | request.query | fastify.js:27:44:27:52 | userInput | This code execution depends on a $@. | fastify.js:26:21:26:33 | request.query | user-provided value | +| fastify.js:27:44:27:52 | userInput | fastify.js:26:21:26:44 | request ... Handler | fastify.js:27:44:27:52 | userInput | This code execution depends on a $@. | fastify.js:26:21:26:44 | request ... Handler | user-provided value | +| fastify.js:32:44:32:52 | userInput | fastify.js:31:21:31:33 | request.query | fastify.js:32:44:32:52 | userInput | This code execution depends on a $@. | fastify.js:31:21:31:33 | request.query | user-provided value | +| fastify.js:32:44:32:52 | userInput | fastify.js:31:21:31:50 | request ... ization | fastify.js:32:44:32:52 | userInput | This code execution depends on a $@. | fastify.js:31:21:31:50 | request ... ization | user-provided value | +| fastify.js:38:44:38:52 | userInput | fastify.js:37:21:37:33 | request.query | fastify.js:38:44:38:52 | userInput | This code execution depends on a $@. | fastify.js:37:21:37:33 | request.query | user-provided value | +| fastify.js:38:44:38:52 | userInput | fastify.js:37:21:37:44 | request ... esponse | fastify.js:38:44:38:52 | userInput | This code execution depends on a $@. | fastify.js:37:21:37:44 | request ... esponse | user-provided value | +| fastify.js:43:44:43:52 | userInput | fastify.js:42:21:42:33 | request.query | fastify.js:43:44:43:52 | userInput | This code execution depends on a $@. | fastify.js:42:21:42:33 | request.query | user-provided value | +| fastify.js:43:44:43:52 | userInput | fastify.js:42:21:42:41 | request ... onError | fastify.js:43:44:43:52 | userInput | This code execution depends on a $@. | fastify.js:42:21:42:41 | request ... onError | user-provided value | +| fastify.js:48:44:48:52 | userInput | fastify.js:47:21:47:33 | request.query | fastify.js:48:44:48:52 | userInput | This code execution depends on a $@. | fastify.js:47:21:47:33 | request.query | user-provided value | +| fastify.js:48:44:48:52 | userInput | fastify.js:47:21:47:43 | request ... Timeout | fastify.js:48:44:48:52 | userInput | This code execution depends on a $@. | fastify.js:47:21:47:43 | request ... Timeout | user-provided value | +| fastify.js:53:46:53:54 | userInput | fastify.js:52:23:52:35 | request.query | fastify.js:53:46:53:54 | userInput | This code execution depends on a $@. | fastify.js:52:23:52:35 | request.query | user-provided value | +| fastify.js:53:46:53:54 | userInput | fastify.js:52:23:52:50 | request ... stAbort | fastify.js:53:46:53:54 | userInput | This code execution depends on a $@. | fastify.js:52:23:52:50 | request ... stAbort | user-provided value | +| fastify.js:58:44:58:52 | userInput | fastify.js:57:21:57:33 | request.query | fastify.js:58:44:58:52 | userInput | This code execution depends on a $@. | fastify.js:57:21:57:33 | request.query | user-provided value | +| fastify.js:58:44:58:52 | userInput | fastify.js:57:21:57:39 | request.query.input | fastify.js:58:44:58:52 | userInput | This code execution depends on a $@. | fastify.js:57:21:57:39 | request.query.input | user-provided value | +| fastify.js:59:23:59:31 | userInput | fastify.js:57:21:57:33 | request.query | fastify.js:59:23:59:31 | userInput | This code execution depends on a $@. | fastify.js:57:21:57:33 | request.query | user-provided value | +| fastify.js:59:23:59:31 | userInput | fastify.js:57:21:57:39 | request.query.input | fastify.js:59:23:59:31 | userInput | This code execution depends on a $@. | fastify.js:57:21:57:39 | request.query.input | user-provided value | +| fastify.js:71:34:71:51 | request.storedCode | fastify.js:71:34:71:51 | request.storedCode | fastify.js:71:34:71:51 | request.storedCode | This code execution depends on a $@. | fastify.js:71:34:71:51 | request.storedCode | user-provided value | +| fastify.js:84:30:84:43 | reply.userCode | fastify.js:84:30:84:43 | reply.userCode | fastify.js:84:30:84:43 | reply.userCode | This code execution depends on a $@. | fastify.js:84:30:84:43 | reply.userCode | user-provided value | +| fastify.js:99:30:99:52 | reply.l ... tedCode | fastify.js:99:30:99:52 | reply.l ... tedCode | fastify.js:99:30:99:52 | reply.l ... tedCode | This code execution depends on a $@. | fastify.js:99:30:99:52 | reply.l ... tedCode | user-provided value | | module.js:9:16:9:29 | req.query.code | module.js:9:16:9:29 | req.query.code | module.js:9:16:9:29 | req.query.code | This code execution depends on a $@. | module.js:9:16:9:29 | req.query.code | user-provided value | | module.js:11:17:11:30 | req.query.code | module.js:11:17:11:30 | req.query.code | module.js:11:17:11:30 | req.query.code | This code execution depends on a $@. | module.js:11:17:11:30 | req.query.code | user-provided value | | react-native.js:8:32:8:38 | tainted | react-native.js:7:17:7:33 | req.param("code") | react-native.js:8:32:8:38 | tainted | This code execution depends on a $@. | react-native.js:7:17:7:33 | req.param("code") | user-provided value | @@ -75,6 +102,40 @@ edges | express.js:27:9:27:35 | taint | express.js:36:15:36:19 | taint | provenance | | | express.js:27:17:27:35 | req.param("wobble") | express.js:27:9:27:35 | taint | provenance | | | express.js:42:30:42:32 | msg | express.js:43:10:43:12 | msg | provenance | | +| fastify.js:4:9:4:43 | userInput | fastify.js:5:44:5:52 | userInput | provenance | | +| fastify.js:4:21:4:33 | request.query | fastify.js:4:9:4:43 | userInput | provenance | | +| fastify.js:4:21:4:43 | request ... Request | fastify.js:4:9:4:43 | userInput | provenance | | +| fastify.js:9:9:9:40 | userInput | fastify.js:10:44:10:52 | userInput | provenance | | +| fastify.js:9:21:9:33 | request.query | fastify.js:9:9:9:40 | userInput | provenance | | +| fastify.js:9:21:9:40 | request.query.onSend | fastify.js:9:9:9:40 | userInput | provenance | | +| fastify.js:15:9:15:44 | userInput | fastify.js:16:44:16:52 | userInput | provenance | | +| fastify.js:15:21:15:33 | request.query | fastify.js:15:9:15:44 | userInput | provenance | | +| fastify.js:15:21:15:44 | request ... Parsing | fastify.js:15:9:15:44 | userInput | provenance | | +| fastify.js:21:9:21:47 | userInput | fastify.js:22:44:22:52 | userInput | provenance | | +| fastify.js:21:21:21:33 | request.query | fastify.js:21:9:21:47 | userInput | provenance | | +| fastify.js:21:21:21:47 | request ... idation | fastify.js:21:9:21:47 | userInput | provenance | | +| fastify.js:26:9:26:44 | userInput | fastify.js:27:44:27:52 | userInput | provenance | | +| fastify.js:26:21:26:33 | request.query | fastify.js:26:9:26:44 | userInput | provenance | | +| fastify.js:26:21:26:44 | request ... Handler | fastify.js:26:9:26:44 | userInput | provenance | | +| fastify.js:31:9:31:50 | userInput | fastify.js:32:44:32:52 | userInput | provenance | | +| fastify.js:31:21:31:33 | request.query | fastify.js:31:9:31:50 | userInput | provenance | | +| fastify.js:31:21:31:50 | request ... ization | fastify.js:31:9:31:50 | userInput | provenance | | +| fastify.js:37:9:37:44 | userInput | fastify.js:38:44:38:52 | userInput | provenance | | +| fastify.js:37:21:37:33 | request.query | fastify.js:37:9:37:44 | userInput | provenance | | +| fastify.js:37:21:37:44 | request ... esponse | fastify.js:37:9:37:44 | userInput | provenance | | +| fastify.js:42:9:42:41 | userInput | fastify.js:43:44:43:52 | userInput | provenance | | +| fastify.js:42:21:42:33 | request.query | fastify.js:42:9:42:41 | userInput | provenance | | +| fastify.js:42:21:42:41 | request ... onError | fastify.js:42:9:42:41 | userInput | provenance | | +| fastify.js:47:9:47:43 | userInput | fastify.js:48:44:48:52 | userInput | provenance | | +| fastify.js:47:21:47:33 | request.query | fastify.js:47:9:47:43 | userInput | provenance | | +| fastify.js:47:21:47:43 | request ... Timeout | fastify.js:47:9:47:43 | userInput | provenance | | +| fastify.js:52:11:52:50 | userInput | fastify.js:53:46:53:54 | userInput | provenance | | +| fastify.js:52:23:52:35 | request.query | fastify.js:52:11:52:50 | userInput | provenance | | +| fastify.js:52:23:52:50 | request ... stAbort | fastify.js:52:11:52:50 | userInput | provenance | | +| fastify.js:57:9:57:39 | userInput | fastify.js:58:44:58:52 | userInput | provenance | | +| fastify.js:57:9:57:39 | userInput | fastify.js:59:23:59:31 | userInput | provenance | | +| fastify.js:57:21:57:33 | request.query | fastify.js:57:9:57:39 | userInput | provenance | | +| fastify.js:57:21:57:39 | request.query.input | fastify.js:57:9:57:39 | userInput | provenance | | | react-native.js:7:7:7:33 | tainted | react-native.js:8:32:8:38 | tainted | provenance | | | react-native.js:7:7:7:33 | tainted | react-native.js:10:23:10:29 | tainted | provenance | | | react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted | provenance | | @@ -144,6 +205,54 @@ nodes | express.js:36:15:36:19 | taint | semmle.label | taint | | express.js:42:30:42:32 | msg | semmle.label | msg | | express.js:43:10:43:12 | msg | semmle.label | msg | +| fastify.js:4:9:4:43 | userInput | semmle.label | userInput | +| fastify.js:4:21:4:33 | request.query | semmle.label | request.query | +| fastify.js:4:21:4:43 | request ... Request | semmle.label | request ... Request | +| fastify.js:5:44:5:52 | userInput | semmle.label | userInput | +| fastify.js:9:9:9:40 | userInput | semmle.label | userInput | +| fastify.js:9:21:9:33 | request.query | semmle.label | request.query | +| fastify.js:9:21:9:40 | request.query.onSend | semmle.label | request.query.onSend | +| fastify.js:10:44:10:52 | userInput | semmle.label | userInput | +| fastify.js:15:9:15:44 | userInput | semmle.label | userInput | +| fastify.js:15:21:15:33 | request.query | semmle.label | request.query | +| fastify.js:15:21:15:44 | request ... Parsing | semmle.label | request ... Parsing | +| fastify.js:16:44:16:52 | userInput | semmle.label | userInput | +| fastify.js:21:9:21:47 | userInput | semmle.label | userInput | +| fastify.js:21:21:21:33 | request.query | semmle.label | request.query | +| fastify.js:21:21:21:47 | request ... idation | semmle.label | request ... idation | +| fastify.js:22:44:22:52 | userInput | semmle.label | userInput | +| fastify.js:26:9:26:44 | userInput | semmle.label | userInput | +| fastify.js:26:21:26:33 | request.query | semmle.label | request.query | +| fastify.js:26:21:26:44 | request ... Handler | semmle.label | request ... Handler | +| fastify.js:27:44:27:52 | userInput | semmle.label | userInput | +| fastify.js:31:9:31:50 | userInput | semmle.label | userInput | +| fastify.js:31:21:31:33 | request.query | semmle.label | request.query | +| fastify.js:31:21:31:50 | request ... ization | semmle.label | request ... ization | +| fastify.js:32:44:32:52 | userInput | semmle.label | userInput | +| fastify.js:37:9:37:44 | userInput | semmle.label | userInput | +| fastify.js:37:21:37:33 | request.query | semmle.label | request.query | +| fastify.js:37:21:37:44 | request ... esponse | semmle.label | request ... esponse | +| fastify.js:38:44:38:52 | userInput | semmle.label | userInput | +| fastify.js:42:9:42:41 | userInput | semmle.label | userInput | +| fastify.js:42:21:42:33 | request.query | semmle.label | request.query | +| fastify.js:42:21:42:41 | request ... onError | semmle.label | request ... onError | +| fastify.js:43:44:43:52 | userInput | semmle.label | userInput | +| fastify.js:47:9:47:43 | userInput | semmle.label | userInput | +| fastify.js:47:21:47:33 | request.query | semmle.label | request.query | +| fastify.js:47:21:47:43 | request ... Timeout | semmle.label | request ... Timeout | +| fastify.js:48:44:48:52 | userInput | semmle.label | userInput | +| fastify.js:52:11:52:50 | userInput | semmle.label | userInput | +| fastify.js:52:23:52:35 | request.query | semmle.label | request.query | +| fastify.js:52:23:52:50 | request ... stAbort | semmle.label | request ... stAbort | +| fastify.js:53:46:53:54 | userInput | semmle.label | userInput | +| fastify.js:57:9:57:39 | userInput | semmle.label | userInput | +| fastify.js:57:21:57:33 | request.query | semmle.label | request.query | +| fastify.js:57:21:57:39 | request.query.input | semmle.label | request.query.input | +| fastify.js:58:44:58:52 | userInput | semmle.label | userInput | +| fastify.js:59:23:59:31 | userInput | semmle.label | userInput | +| fastify.js:71:34:71:51 | request.storedCode | semmle.label | request.storedCode | +| fastify.js:84:30:84:43 | reply.userCode | semmle.label | reply.userCode | +| fastify.js:99:30:99:52 | reply.l ... tedCode | semmle.label | reply.l ... tedCode | | module.js:9:16:9:29 | req.query.code | semmle.label | req.query.code | | module.js:11:17:11:30 | req.query.code | semmle.label | req.query.code | | react-native.js:7:7:7:33 | tainted | semmle.label | tainted | diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected index ba973943e124..0f52acfa6cde 100644 --- a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected @@ -11,6 +11,40 @@ edges | express.js:27:9:27:35 | taint | express.js:36:15:36:19 | taint | provenance | | | express.js:27:17:27:35 | req.param("wobble") | express.js:27:9:27:35 | taint | provenance | | | express.js:42:30:42:32 | msg | express.js:43:10:43:12 | msg | provenance | | +| fastify.js:4:9:4:43 | userInput | fastify.js:5:44:5:52 | userInput | provenance | | +| fastify.js:4:21:4:33 | request.query | fastify.js:4:9:4:43 | userInput | provenance | | +| fastify.js:4:21:4:43 | request ... Request | fastify.js:4:9:4:43 | userInput | provenance | | +| fastify.js:9:9:9:40 | userInput | fastify.js:10:44:10:52 | userInput | provenance | | +| fastify.js:9:21:9:33 | request.query | fastify.js:9:9:9:40 | userInput | provenance | | +| fastify.js:9:21:9:40 | request.query.onSend | fastify.js:9:9:9:40 | userInput | provenance | | +| fastify.js:15:9:15:44 | userInput | fastify.js:16:44:16:52 | userInput | provenance | | +| fastify.js:15:21:15:33 | request.query | fastify.js:15:9:15:44 | userInput | provenance | | +| fastify.js:15:21:15:44 | request ... Parsing | fastify.js:15:9:15:44 | userInput | provenance | | +| fastify.js:21:9:21:47 | userInput | fastify.js:22:44:22:52 | userInput | provenance | | +| fastify.js:21:21:21:33 | request.query | fastify.js:21:9:21:47 | userInput | provenance | | +| fastify.js:21:21:21:47 | request ... idation | fastify.js:21:9:21:47 | userInput | provenance | | +| fastify.js:26:9:26:44 | userInput | fastify.js:27:44:27:52 | userInput | provenance | | +| fastify.js:26:21:26:33 | request.query | fastify.js:26:9:26:44 | userInput | provenance | | +| fastify.js:26:21:26:44 | request ... Handler | fastify.js:26:9:26:44 | userInput | provenance | | +| fastify.js:31:9:31:50 | userInput | fastify.js:32:44:32:52 | userInput | provenance | | +| fastify.js:31:21:31:33 | request.query | fastify.js:31:9:31:50 | userInput | provenance | | +| fastify.js:31:21:31:50 | request ... ization | fastify.js:31:9:31:50 | userInput | provenance | | +| fastify.js:37:9:37:44 | userInput | fastify.js:38:44:38:52 | userInput | provenance | | +| fastify.js:37:21:37:33 | request.query | fastify.js:37:9:37:44 | userInput | provenance | | +| fastify.js:37:21:37:44 | request ... esponse | fastify.js:37:9:37:44 | userInput | provenance | | +| fastify.js:42:9:42:41 | userInput | fastify.js:43:44:43:52 | userInput | provenance | | +| fastify.js:42:21:42:33 | request.query | fastify.js:42:9:42:41 | userInput | provenance | | +| fastify.js:42:21:42:41 | request ... onError | fastify.js:42:9:42:41 | userInput | provenance | | +| fastify.js:47:9:47:43 | userInput | fastify.js:48:44:48:52 | userInput | provenance | | +| fastify.js:47:21:47:33 | request.query | fastify.js:47:9:47:43 | userInput | provenance | | +| fastify.js:47:21:47:43 | request ... Timeout | fastify.js:47:9:47:43 | userInput | provenance | | +| fastify.js:52:11:52:50 | userInput | fastify.js:53:46:53:54 | userInput | provenance | | +| fastify.js:52:23:52:35 | request.query | fastify.js:52:11:52:50 | userInput | provenance | | +| fastify.js:52:23:52:50 | request ... stAbort | fastify.js:52:11:52:50 | userInput | provenance | | +| fastify.js:57:9:57:39 | userInput | fastify.js:58:44:58:52 | userInput | provenance | | +| fastify.js:57:9:57:39 | userInput | fastify.js:59:23:59:31 | userInput | provenance | | +| fastify.js:57:21:57:33 | request.query | fastify.js:57:9:57:39 | userInput | provenance | | +| fastify.js:57:21:57:39 | request.query.input | fastify.js:57:9:57:39 | userInput | provenance | | | react-native.js:7:7:7:33 | tainted | react-native.js:8:32:8:38 | tainted | provenance | | | react-native.js:7:7:7:33 | tainted | react-native.js:10:23:10:29 | tainted | provenance | | | react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted | provenance | | @@ -82,6 +116,54 @@ nodes | express.js:36:15:36:19 | taint | semmle.label | taint | | express.js:42:30:42:32 | msg | semmle.label | msg | | express.js:43:10:43:12 | msg | semmle.label | msg | +| fastify.js:4:9:4:43 | userInput | semmle.label | userInput | +| fastify.js:4:21:4:33 | request.query | semmle.label | request.query | +| fastify.js:4:21:4:43 | request ... Request | semmle.label | request ... Request | +| fastify.js:5:44:5:52 | userInput | semmle.label | userInput | +| fastify.js:9:9:9:40 | userInput | semmle.label | userInput | +| fastify.js:9:21:9:33 | request.query | semmle.label | request.query | +| fastify.js:9:21:9:40 | request.query.onSend | semmle.label | request.query.onSend | +| fastify.js:10:44:10:52 | userInput | semmle.label | userInput | +| fastify.js:15:9:15:44 | userInput | semmle.label | userInput | +| fastify.js:15:21:15:33 | request.query | semmle.label | request.query | +| fastify.js:15:21:15:44 | request ... Parsing | semmle.label | request ... Parsing | +| fastify.js:16:44:16:52 | userInput | semmle.label | userInput | +| fastify.js:21:9:21:47 | userInput | semmle.label | userInput | +| fastify.js:21:21:21:33 | request.query | semmle.label | request.query | +| fastify.js:21:21:21:47 | request ... idation | semmle.label | request ... idation | +| fastify.js:22:44:22:52 | userInput | semmle.label | userInput | +| fastify.js:26:9:26:44 | userInput | semmle.label | userInput | +| fastify.js:26:21:26:33 | request.query | semmle.label | request.query | +| fastify.js:26:21:26:44 | request ... Handler | semmle.label | request ... Handler | +| fastify.js:27:44:27:52 | userInput | semmle.label | userInput | +| fastify.js:31:9:31:50 | userInput | semmle.label | userInput | +| fastify.js:31:21:31:33 | request.query | semmle.label | request.query | +| fastify.js:31:21:31:50 | request ... ization | semmle.label | request ... ization | +| fastify.js:32:44:32:52 | userInput | semmle.label | userInput | +| fastify.js:37:9:37:44 | userInput | semmle.label | userInput | +| fastify.js:37:21:37:33 | request.query | semmle.label | request.query | +| fastify.js:37:21:37:44 | request ... esponse | semmle.label | request ... esponse | +| fastify.js:38:44:38:52 | userInput | semmle.label | userInput | +| fastify.js:42:9:42:41 | userInput | semmle.label | userInput | +| fastify.js:42:21:42:33 | request.query | semmle.label | request.query | +| fastify.js:42:21:42:41 | request ... onError | semmle.label | request ... onError | +| fastify.js:43:44:43:52 | userInput | semmle.label | userInput | +| fastify.js:47:9:47:43 | userInput | semmle.label | userInput | +| fastify.js:47:21:47:33 | request.query | semmle.label | request.query | +| fastify.js:47:21:47:43 | request ... Timeout | semmle.label | request ... Timeout | +| fastify.js:48:44:48:52 | userInput | semmle.label | userInput | +| fastify.js:52:11:52:50 | userInput | semmle.label | userInput | +| fastify.js:52:23:52:35 | request.query | semmle.label | request.query | +| fastify.js:52:23:52:50 | request ... stAbort | semmle.label | request ... stAbort | +| fastify.js:53:46:53:54 | userInput | semmle.label | userInput | +| fastify.js:57:9:57:39 | userInput | semmle.label | userInput | +| fastify.js:57:21:57:33 | request.query | semmle.label | request.query | +| fastify.js:57:21:57:39 | request.query.input | semmle.label | request.query.input | +| fastify.js:58:44:58:52 | userInput | semmle.label | userInput | +| fastify.js:59:23:59:31 | userInput | semmle.label | userInput | +| fastify.js:71:34:71:51 | request.storedCode | semmle.label | request.storedCode | +| fastify.js:84:30:84:43 | reply.userCode | semmle.label | reply.userCode | +| fastify.js:99:30:99:52 | reply.l ... tedCode | semmle.label | reply.l ... tedCode | | module.js:9:16:9:29 | req.query.code | semmle.label | req.query.code | | module.js:11:17:11:30 | req.query.code | semmle.label | req.query.code | | react-native.js:7:7:7:33 | tainted | semmle.label | tainted | diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/fastify.js b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/fastify.js new file mode 100644 index 000000000000..bb5577c7acc1 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/fastify.js @@ -0,0 +1,103 @@ +const fastify = require('fastify')({ logger: true }); + +fastify.addHook('onRequest', async (request, reply) => { + const userInput = request.query.onRequest; // $ Source[js/code-injection] + if (userInput) request.evalResult = eval(userInput); // $ Alert[js/code-injection] +}); + +fastify.addHook('onSend', async (request, reply, payload) => { + const userInput = request.query.onSend; // $ Source[js/code-injection] + if (userInput) request.evalResult = eval(userInput); // $ Alert[js/code-injection] + return JSON.stringify({ ...JSON.parse(payload), onSend: request.evalResult }); +}); + +fastify.addHook('preParsing', async (request, reply, payload) => { + const userInput = request.query.preParsing; // $ Source[js/code-injection] + if (userInput) request.evalResult = eval(userInput); // $ Alert[js/code-injection] + return payload; +}); + +fastify.addHook('preValidation', async (request, reply) => { + const userInput = request.query.preValidation; // $ Source[js/code-injection] + if (userInput) request.evalResult = eval(userInput); // $ Alert[js/code-injection] +}); + +fastify.addHook('preHandler', async (request, reply) => { + const userInput = request.query.preHandler; // $ Source[js/code-injection] + if (userInput) request.evalResult = eval(userInput); // $ Alert[js/code-injection] +}); + +fastify.addHook('preSerialization', async (request, reply, payload) => { + const userInput = request.query.preSerialization; // $ Source[js/code-injection] + if (userInput) request.evalResult = eval(userInput); // $ Alert[js/code-injection] + return payload; +}); + +fastify.addHook('onResponse', async (request, reply) => { + const userInput = request.query.onResponse; // $ Source[js/code-injection] + if (userInput) request.evalResult = eval(userInput); // $ Alert[js/code-injection] +}); + +fastify.addHook('onError', async (request, reply, error) => { + const userInput = request.query.onError; // $ Source[js/code-injection] + if (userInput) request.evalResult = eval(userInput); // $ Alert[js/code-injection] +}); + +fastify.addHook('onTimeout', async (request, reply) => { + const userInput = request.query.onTimeout; // $ Source[js/code-injection] + if (userInput) request.evalResult = eval(userInput); // $ Alert[js/code-injection] +}); + +fastify.addHook('onRequestAbort', (request, done) => { + const userInput = request.query.onRequestAbort; // $ Source[js/code-injection] + if (userInput) request.evalResult = eval(userInput); // $ Alert[js/code-injection] +}); + +fastify.get('/dangerous', async (request, reply) => { + const userInput = request.query.input; // $ Source[js/code-injection] + if (userInput) request.evalResult = eval(userInput); // $ Alert[js/code-injection] + const result = eval(userInput); // $ Alert[js/code-injection] + return { result }; +}); + + +// Store user input in request object +fastify.addHook('preHandler', async (request, reply) => { + request.storedCode = request.query.storedCode; +}); +fastify.get('/flow-through-request', async (request, reply) => { + // Use the stored code from previous hook + if (request.storedCode) { + const evaluatedResult = eval(request.storedCode); // $ Alert[js/code-injection] + return { result: evaluatedResult }; + } + return { result: null }; +}); + +// Store user input in reply object +fastify.addHook('onRequest', async (request, reply) => { + reply.userCode = request.query.replyCode; +}); +fastify.get('/flow-through-reply', async (request, reply) => { + // Use the code stored in reply object + if (reply.userCode) { + const replyResult = eval(reply.userCode); // $ Alert[js/code-injection] + return { result: replyResult }; + } + return { result: null }; +}); + + +// Store user input in reply object +fastify.addHook('onRequest', async (request, reply) => { + reply.locals = reply.locals || {}; + reply.locals.nestedCode = request.query.replyCode; +}); +fastify.get('/flow-through-reply', async (request, reply) => { + // Use the code stored in reply object + if (reply.locals && reply.locals.nestedCode) { + const replyResult = eval(reply.locals.nestedCode); // $ Alert[js/code-injection] + return { result: replyResult }; + } + return { result: null }; +});