diff --git a/.changeset/codeql-admin-rate-limit-models.md b/.changeset/codeql-admin-rate-limit-models.md new file mode 100644 index 00000000..a8ac269a --- /dev/null +++ b/.changeset/codeql-admin-rate-limit-models.md @@ -0,0 +1,5 @@ +--- +"nostream": patch +--- + +fix: teach CodeQL to recognize custom admin rate limit middleware diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml new file mode 100644 index 00000000..c34da3e4 --- /dev/null +++ b/.github/codeql/codeql-config.yml @@ -0,0 +1,8 @@ +name: nostream-codeql + +packs: + javascript-typescript: + - cameri/nostream-javascript-models + +queries: + - uses: security-and-quality diff --git a/.github/codeql/extensions/nostream-javascript-models/NostreamRateLimitingMiddleware.qll b/.github/codeql/extensions/nostream-javascript-models/NostreamRateLimitingMiddleware.qll new file mode 100644 index 00000000..7dc94f95 --- /dev/null +++ b/.github/codeql/extensions/nostream-javascript-models/NostreamRateLimitingMiddleware.qll @@ -0,0 +1,50 @@ +/** + * Models Nostream's custom Redis-backed rate limit middleware for CodeQL's + * js/missing-rate-limiting query. + * + * CodeQL only recognizes popular npm rate limiters by default. Nostream uses + * in-repo middleware that calls isAdminRateLimited() / isRateLimited(). + */ + +import javascript +import semmle.javascript.security.dataflow.MissingRateLimiting + +private predicate isNostreamRateLimitCheck(CallExpr call, string calleeName) { + call.getCalleeName() = calleeName +} + +private predicate isInFile(Function fn, string fileSuffix) { + fn.getFile().getRelativePath().regexpMatch(".*/" + fileSuffix + "$") +} + +/** + * An admin route middleware function that invokes isAdminRateLimited(). + */ +class NostreamAdminRateLimiterFunction extends DataFlow::FunctionNode { + NostreamAdminRateLimiterFunction() { + exists(CallExpr call | + isNostreamRateLimitCheck(call, "isAdminRateLimited") and + call.getEnclosingFunction() = this.getFunction() and + isInFile(this.getFunction(), "admin-rate-limit-middleware.ts") + ) + } +} + +/** + * A connection-level middleware function that invokes isRateLimited(). + */ +class NostreamConnectionRateLimiterFunction extends DataFlow::FunctionNode { + NostreamConnectionRateLimiterFunction() { + exists(CallExpr call | + isNostreamRateLimitCheck(call, "isRateLimited") and + call.getEnclosingFunction() = this.getFunction() and + isInFile(this.getFunction(), "rate-limiter-middleware.ts") + ) + } +} + +class NostreamAdminRateLimitMiddleware extends RateLimitingMiddleware instanceof NostreamAdminRateLimiterFunction { +} + +class NostreamConnectionRateLimitMiddleware extends RateLimitingMiddleware instanceof NostreamConnectionRateLimiterFunction { +} diff --git a/.github/codeql/extensions/nostream-javascript-models/codeql-pack.yml b/.github/codeql/extensions/nostream-javascript-models/codeql-pack.yml new file mode 100644 index 00000000..f0666693 --- /dev/null +++ b/.github/codeql/extensions/nostream-javascript-models/codeql-pack.yml @@ -0,0 +1,5 @@ +name: cameri/nostream-javascript-models +version: 0.0.1 +library: true +dependencies: + codeql/javascript-all: "*" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index d362fe8d..a9db2d1b 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -85,17 +85,7 @@ jobs: with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} - # If you wish to specify custom queries, you can do so here or in - # a config file. By default, queries listed here will override any - # specified in a config file. - # Prefix the list here with "+" to use these queries and those in - # the config file. - - # For more details on CodeQL's query packs, refer to: - # https://docs.github.com/en/code-security/code-scanning/ - # automatically-scanning-your-code-for-vulnerabilities-and-errors/ - # configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality + config-file: ./.github/codeql/codeql-config.yml # If the analyze step fails for one of the languages you are analyzing # with "We were unable to automatically build your code", modify the