Skip to content

Commit f5fa010

Browse files
dtorres-fgfclaude
andcommitted
Add addContentTypeParser, drop after and ready, and date the change note in UTC
`addContentTypeParser` returns the server, so it belongs in the list. Its type declaration says `void`, which is why it was missed: it is declared as a property of an interface type rather than as a method, so it did not look like the `set*` family. Checking the rest of the instance against the runtime rather than against the declarations then showed that `ready` never returns the server, in either of its call forms, despite the declaration saying it does, and that `after` returns it only when given a callback. Both are now excluded, which also makes the predicate exactly what its name says. `onClose` returns the server too, but it registers a hook, so it is excluded for the same reason as `addHook`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 2b19c84 commit f5fa010

4 files changed

Lines changed: 16 additions & 13 deletions

File tree

javascript/ql/lib/change-notes/2026-09-11-fastify-chainable-config-methods.md renamed to javascript/ql/lib/change-notes/2026-09-10-fastify-chainable-config-methods.md

File renamed without changes.

javascript/ql/lib/semmle/javascript/frameworks/Fastify.qll

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,23 @@ module Fastify {
2222
}
2323

2424
/**
25-
* Gets the name of a chainable Fastify server method, that is, a configuration or
26-
* lifecycle method that returns the same server instance it was called on, so that a
27-
* call to it still refers to that server.
25+
* Gets the name of a chainable Fastify configuration method, that is, a method that
26+
* configures the server instance and returns that same instance, so that a call to it
27+
* still refers to the server.
2828
*
29-
* Route-registering methods such as `register`, `addHook`, and the shorthand route
30-
* methods return the server as well, but they are deliberately excluded here because
31-
* they already have a meaning in the routing model for Fastify.
29+
* Plugin, hook and route registration (`register`, `addHook`, `onClose`, and the
30+
* shorthand route methods) returns the server as well, but is deliberately excluded
31+
* here, because it already has a meaning in the routing model for Fastify. The
32+
* lifecycle methods `after` and `ready` are excluded too: `after` returns the server
33+
* only when it is given a callback, and `ready` never does.
3234
*/
33-
private string chainableServerMethodName() {
35+
private string chainableConfigMethodName() {
3436
result =
3537
[
36-
"withTypeProvider", "addSchema", "addHttpMethod", "decorate", "decorateRequest",
37-
"decorateReply", "setValidatorCompiler", "setSerializerCompiler", "setSchemaController",
38-
"setReplySerializer", "setSchemaErrorFormatter", "setErrorHandler", "setNotFoundHandler",
39-
"setGenReqId", "setChildLoggerFactory", "after", "ready"
38+
"withTypeProvider", "addSchema", "addHttpMethod", "addContentTypeParser", "decorate",
39+
"decorateRequest", "decorateReply", "setValidatorCompiler", "setSerializerCompiler",
40+
"setSchemaController", "setReplySerializer", "setSchemaErrorFormatter", "setErrorHandler",
41+
"setNotFoundHandler", "setGenReqId", "setChildLoggerFactory"
4042
]
4143
}
4244

@@ -53,7 +55,7 @@ module Fastify {
5355
// server.withTypeProvider<T>(), server.setValidatorCompiler(...), and friends return
5456
// the server itself, so the result of such a call still refers to it.
5557
t.start() and
56-
result = server(creation).getAMethodCall(chainableServerMethodName())
58+
result = server(creation).getAMethodCall(chainableConfigMethodName())
5759
or
5860
exists(DataFlow::TypeTracker t2 | result = server(creation, t2).track(t2, t))
5961
}

javascript/ql/test/query-tests/Security/CWE-770/MissingRateLimit/MissingRateLimiting.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@
1212
| tst.js:111:28:111:44 | expensiveHandler1 | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization |
1313
| tst.js:116:39:116:55 | expensiveHandler1 | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization |
1414
| tst.js:130:35:130:51 | expensiveHandler1 | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization |
15-
| tst.js:160:35:160:51 | expensiveHandler1 | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization |
15+
| tst.js:161:35:161:51 | expensiveHandler1 | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization |

javascript/ql/test/query-tests/Security/CWE-770/MissingRateLimit/tst.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ fastifyApp5.get('/no-rate-limit', expensiveHandler1); // $ Alert
133133
const fastifyApp6 = require('fastify')()
134134
.withTypeProvider()
135135
.setValidatorCompiler(compiler)
136+
.addContentTypeParser('application/json', parser)
136137
.decorate('answer', 42);
137138

138139
fastifyApp6.register(require('@fastify/rate-limit'));

0 commit comments

Comments
 (0)