From 5a77128a8bcf9b090d76b6dc09cf53f0ecd20815 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 23 Mar 2026 11:27:05 +0000 Subject: [PATCH 01/49] C++: Disable cpp/implicit-function-declaration on BMN databases. --- .../Underspecified Functions/ImplicitFunctionDeclaration.ql | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql b/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql index 6a55557cf70..007ef71a163 100644 --- a/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql +++ b/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql @@ -17,6 +17,11 @@ import TooFewArguments import TooManyArguments import semmle.code.cpp.commons.Exclusions +/* + * This query is not compatible with build mode: none databases, and has + * no results on those databases. + */ + predicate locInfo(Locatable e, File file, int line, int col) { e.getFile() = file and e.getLocation().getStartLine() = line and @@ -39,6 +44,7 @@ predicate isCompiledAsC(File f) { from FunctionDeclarationEntry fdeIm, FunctionCall fc where isCompiledAsC(fdeIm.getFile()) and + not any(Compilation c).buildModeNone() and not isFromMacroDefinition(fc) and fdeIm.isImplicit() and sameLocation(fdeIm, fc) and From 39056e44771373f23bcd8a561bbf60a6c0122e60 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 23 Mar 2026 12:28:12 +0000 Subject: [PATCH 02/49] C++: Change note. --- .../change-notes/2026-03-23-implicit-function-declaration.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 cpp/ql/src/change-notes/2026-03-23-implicit-function-declaration.md diff --git a/cpp/ql/src/change-notes/2026-03-23-implicit-function-declaration.md b/cpp/ql/src/change-notes/2026-03-23-implicit-function-declaration.md new file mode 100644 index 00000000000..8c2c431ec24 --- /dev/null +++ b/cpp/ql/src/change-notes/2026-03-23-implicit-function-declaration.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The "Implicit function declaration" (`cpp/implicit-function-declaration`) query no longer produces results on `build mode: none` databases. These results were found to be very noisy and fundamentally imprecise in this mode. From 10fddc7b960879d188e58cdf02a9a532cd844cea Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 26 Mar 2026 11:40:11 +0000 Subject: [PATCH 03/49] Add barriers and barrier guards to MaD format explanations --- cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll | 10 +++++++++- .../code/csharp/dataflow/internal/ExternalFlow.qll | 11 +++++++++-- go/ql/lib/semmle/go/dataflow/ExternalFlow.qll | 10 +++++++++- .../ql/lib/semmle/code/java/dataflow/ExternalFlow.qll | 11 +++++++++-- .../frameworks/data/internal/ApiGraphModels.qll | 7 ++++++- .../frameworks/data/internal/ApiGraphModels.qll | 7 ++++++- .../ruby/frameworks/data/internal/ApiGraphModels.qll | 7 ++++++- .../codeql/rust/dataflow/internal/ModelsAsData.qll | 11 +++++++++-- 8 files changed, 63 insertions(+), 11 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll index 7cf3b937ac5..1ec501a85dd 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll @@ -10,6 +10,10 @@ * `namespace; type; subtypes; name; signature; ext; input; kind` * - Summaries: * `namespace; type; subtypes; name; signature; ext; input; output; kind` + * - Barriers: + * `namespace; type; subtypes; name; signature; ext; output; kind; provenance` + * - BarrierGuards: + * `namespace; type; subtypes; name; signature; ext; input; acceptingvalue; kind; provenance` * * The interpretation of a row is similar to API-graphs with a left-to-right * reading. @@ -86,7 +90,11 @@ * value, and * - flow from the _second_ indirection of the 0th argument to the first * indirection of the return value, etc. - * 8. The `kind` column is a tag that can be referenced from QL to determine to + * 8. The `acceptingvalue` column of barrier guard models specifies the condition + * under which the guard accepts or blocks flow. It can be one of "true" or + * "false". In the future "no-exception", "not-zero", "null", "not-null" may be + * supported. + * 9. The `kind` column is a tag that can be referenced from QL to determine to * which classes the interpreted elements should be added. For example, for * sources "remote" indicates a default remote flow source, and for summaries * "taint" indicates a default additional taint step and "value" indicates a diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll index 024e9cf119d..2b4264fc432 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll @@ -11,6 +11,10 @@ * `namespace; type; subtypes; name; signature; ext; input; kind; provenance` * - Summaries: * `namespace; type; subtypes; name; signature; ext; input; output; kind; provenance` + * - Barriers: + * `namespace; type; subtypes; name; signature; ext; output; kind; provenance` + * - BarrierGuards: + * `namespace; type; subtypes; name; signature; ext; input; acceptingvalue; kind; provenance` * - Neutrals: * `namespace; type; name; signature; kind; provenance` * A neutral is used to indicate that a callable is neutral with respect to flow (no summary), source (is not a source) or sink (is not a sink). @@ -69,14 +73,17 @@ * - "Field[f]": Selects the contents of field `f`. * - "Property[p]": Selects the contents of property `p`. * - * 8. The `kind` column is a tag that can be referenced from QL to determine to + * 8. The `acceptingvalue` column of barrier guard models specifies the condition + * under which the guard accepts or blocks flow. It can be one of "true" or + * "false", "no-exception", "not-zero", "null", "not-null". + * 9. The `kind` column is a tag that can be referenced from QL to determine to * which classes the interpreted elements should be added. For example, for * sources "remote" indicates a default remote flow source, and for summaries * "taint" indicates a default additional taint step and "value" indicates a * globally applicable value-preserving step. For neutrals the kind can be `summary`, * `source` or `sink` to indicate that the neutral is neutral with respect to * flow (no summary), source (is not a source) or sink (is not a sink). - * 9. The `provenance` column is a tag to indicate the origin and verification of a model. + * 10. The `provenance` column is a tag to indicate the origin and verification of a model. * The format is {origin}-{verification} or just "manual" where the origin describes * the origin of the model and verification describes how the model has been verified. * Some examples are: diff --git a/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll b/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll index e1170aeda24..3812b3df449 100644 --- a/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll +++ b/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll @@ -11,6 +11,10 @@ * `package; type; subtypes; name; signature; ext; input; kind; provenance` * - Summaries: * `package; type; subtypes; name; signature; ext; input; output; kind; provenance` + * - Barriers: + * `package; type; subtypes; name; signature; ext; output; kind; provenance` + * - BarrierGuards: + * `package; type; subtypes; name; signature; ext; input; acceptingvalue; kind; provenance` * - Neutrals: * `package; type; name; signature; kind; provenance` * A neutral is used to indicate that a callable is neutral with respect to flow (no summary), source (is not a source) or sink (is not a sink). @@ -78,7 +82,11 @@ * - "MapValue": Selects a value in a map. * - "Dereference": Selects the value referenced by a pointer. * - * 8. The `kind` column is a tag that can be referenced from QL to determine to + * 8. The `acceptingvalue` column of barrier guard models specifies the condition + * under which the guard accepts or blocks flow. It can be one of "true" or + * "false". In the future "no-exception", "not-zero", "null", "not-null" may be + * supported. + * 9. The `kind` column is a tag that can be referenced from QL to determine to * which classes the interpreted elements should be added. For example, for * sources "remote" indicates a default remote flow source, and for summaries * "taint" indicates a default additional taint step and "value" indicates a diff --git a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll index 1536c81aa08..45db15897f7 100644 --- a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll @@ -11,6 +11,10 @@ * `package; type; subtypes; name; signature; ext; input; kind; provenance` * - Summaries: * `package; type; subtypes; name; signature; ext; input; output; kind; provenance` + * - Barriers: + * `package; type; subtypes; name; signature; ext; output; kind; provenance` + * - BarrierGuards: + * `package; type; subtypes; name; signature; ext; input; acceptingvalue; kind; provenance` * - Neutrals: * `package; type; name; signature; kind; provenance` * A neutral is used to indicate that a callable is neutral with respect to flow (no summary), source (is not a source) or sink (is not a sink). @@ -69,14 +73,17 @@ * in the given range. The range is inclusive at both ends. * - "ReturnValue": Selects the return value of a call to the selected element. * - "Element": Selects the collection elements of the selected element. - * 8. The `kind` column is a tag that can be referenced from QL to determine to + * 8. The `acceptingvalue` column of barrier guard models specifies the condition + * under which the guard accepts or blocks flow. It can be one of "true" or + * "false", "no-exception", "not-zero", "null", "not-null". + * 9. The `kind` column is a tag that can be referenced from QL to determine to * which classes the interpreted elements should be added. For example, for * sources "remote" indicates a default remote flow source, and for summaries * "taint" indicates a default additional taint step and "value" indicates a * globally applicable value-preserving step. For neutrals the kind can be `summary`, * `source` or `sink` to indicate that the neutral is neutral with respect to * flow (no summary), source (is not a source) or sink (is not a sink). - * 9. The `provenance` column is a tag to indicate the origin and verification of a model. + * 10. The `provenance` column is a tag to indicate the origin and verification of a model. * The format is {origin}-{verification} or just "manual" where the origin describes * the origin of the model and verification describes how the model has been verified. * Some examples are: diff --git a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll index 60fe40e716d..34bf3267522 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll @@ -10,6 +10,10 @@ * `type, path, kind` * - Summaries: * `type, path, input, output, kind` + * - Barriers: + * `type, path, kind` + * - BarrierGuards: + * `type, path, branch, kind` * - Types: * `type1, type2, path` * @@ -42,7 +46,8 @@ * 3. The `input` and `output` columns specify how data enters and leaves the element selected by the * first `(type, path)` tuple. Both strings are `.`-separated access paths * of the same syntax as the `path` column. - * 4. The `kind` column is a tag that can be referenced from QL to determine to + * 4. The `branch` column of barrier guard models specifies which branch of the guard is blocking flow. It can be "true" or "false". + * 5. The `kind` column is a tag that can be referenced from QL to determine to * which classes the interpreted elements should be added. For example, for * sources `"remote"` indicates a default remote flow source, and for summaries * `"taint"` indicates a default additional taint step and `"value"` indicates a diff --git a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll index 60fe40e716d..34bf3267522 100644 --- a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll +++ b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll @@ -10,6 +10,10 @@ * `type, path, kind` * - Summaries: * `type, path, input, output, kind` + * - Barriers: + * `type, path, kind` + * - BarrierGuards: + * `type, path, branch, kind` * - Types: * `type1, type2, path` * @@ -42,7 +46,8 @@ * 3. The `input` and `output` columns specify how data enters and leaves the element selected by the * first `(type, path)` tuple. Both strings are `.`-separated access paths * of the same syntax as the `path` column. - * 4. The `kind` column is a tag that can be referenced from QL to determine to + * 4. The `branch` column of barrier guard models specifies which branch of the guard is blocking flow. It can be "true" or "false". + * 5. The `kind` column is a tag that can be referenced from QL to determine to * which classes the interpreted elements should be added. For example, for * sources `"remote"` indicates a default remote flow source, and for summaries * `"taint"` indicates a default additional taint step and `"value"` indicates a diff --git a/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll b/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll index 60fe40e716d..34bf3267522 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll @@ -10,6 +10,10 @@ * `type, path, kind` * - Summaries: * `type, path, input, output, kind` + * - Barriers: + * `type, path, kind` + * - BarrierGuards: + * `type, path, branch, kind` * - Types: * `type1, type2, path` * @@ -42,7 +46,8 @@ * 3. The `input` and `output` columns specify how data enters and leaves the element selected by the * first `(type, path)` tuple. Both strings are `.`-separated access paths * of the same syntax as the `path` column. - * 4. The `kind` column is a tag that can be referenced from QL to determine to + * 4. The `branch` column of barrier guard models specifies which branch of the guard is blocking flow. It can be "true" or "false". + * 5. The `kind` column is a tag that can be referenced from QL to determine to * which classes the interpreted elements should be added. For example, for * sources `"remote"` indicates a default remote flow source, and for summaries * `"taint"` indicates a default additional taint step and `"value"` indicates a diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll b/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll index 4d28dd8de81..a21d50ed8ad 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll @@ -9,6 +9,10 @@ * `path; input; kind; provenance` * - Summaries: * `path; input; output; kind; provenance` + * - Barriers: + * `path; output; kind; provenance` + * - BarrierGuards: + * `path; input; branch; kind; provenance` * * The interpretation of a row is similar to API-graphs with a left-to-right * reading. @@ -34,12 +38,15 @@ * - `Field[i]`: the `i`th element of a tuple. * - `Reference`: the referenced value. * - `Future`: the value being computed asynchronously. - * 3. The `kind` column is a tag that can be referenced from QL to determine to + * 3. The `branch` column of barrier guard models specifies which branch of the + * guard is blocking flow. It can be "true" or "false". In the future + * "no-exception", "not-zero", "null", "not-null" may be supported. + * 4. The `kind` column is a tag that can be referenced from QL to determine to * which classes the interpreted elements should be added. For example, for * sources `"remote"` indicates a default remote flow source, and for summaries * `"taint"` indicates a default additional taint step and `"value"` indicates a * globally applicable value-preserving step. - * 4. The `provenance` column is mainly used internally, and should be set to `"manual"` for + * 5. The `provenance` column is mainly used internally, and should be set to `"manual"` for * all custom models. */ From 61b13d570272fe63c193a1b6b2158cf20a959883 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 26 Mar 2026 11:40:42 +0000 Subject: [PATCH 04/49] C++: Add provenance to MaD format explanation --- cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll index 1ec501a85dd..b36c37d8114 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll @@ -99,6 +99,15 @@ * sources "remote" indicates a default remote flow source, and for summaries * "taint" indicates a default additional taint step and "value" indicates a * globally applicable value-preserving step. + * 10. The `provenance` column is a tag to indicate the origin and verification of a model. + * The format is {origin}-{verification} or just "manual" where the origin describes + * the origin of the model and verification describes how the model has been verified. + * Some examples are: + * - "df-generated": The model has been generated by the model generator tool. + * - "df-manual": The model has been generated by the model generator and verified by a human. + * - "manual": The model has been written by hand. + * This information is used in a heuristic for dataflow analysis to determine, if a + * model or source code should be used for determining flow. */ import cpp From 805d2ec46cbe4c5aae54c817f59811f1595b250b Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 26 Mar 2026 11:41:59 +0000 Subject: [PATCH 05/49] Go: Add provenance to MaD format explanation --- go/ql/lib/semmle/go/dataflow/ExternalFlow.qll | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll b/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll index 3812b3df449..23e08ce5cbf 100644 --- a/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll +++ b/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll @@ -91,6 +91,15 @@ * sources "remote" indicates a default remote flow source, and for summaries * "taint" indicates a default additional taint step and "value" indicates a * globally applicable value-preserving step. + * 10. The `provenance` column is a tag to indicate the origin and verification of a model. + * The format is {origin}-{verification} or just "manual" where the origin describes + * the origin of the model and verification describes how the model has been verified. + * Some examples are: + * - "df-generated": The model has been generated by the model generator tool. + * - "df-manual": The model has been generated by the model generator and verified by a human. + * - "manual": The model has been written by hand. + * This information is used in a heuristic for dataflow analysis to determine, if a + * model or source code should be used for determining flow. */ overlay[local?] module; From df842665b7156aa20de94972d39698a74326462b Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 26 Mar 2026 11:42:13 +0000 Subject: [PATCH 06/49] Rust: Add neutrals to MaD format explanation --- rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll b/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll index a21d50ed8ad..a43495ac784 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll @@ -13,6 +13,9 @@ * `path; output; kind; provenance` * - BarrierGuards: * `path; input; branch; kind; provenance` + * - Neutrals: + * `package; type; name; signature; kind; provenance` + * A neutral is used to indicate that a callable is neutral with respect to flow (no summary), source (is not a source) or sink (is not a sink). * * The interpretation of a row is similar to API-graphs with a left-to-right * reading. From e680d49c93334f38134c1e7da000e0e18db42fc3 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 26 Mar 2026 12:08:54 +0000 Subject: [PATCH 07/49] Shared: document extensible relations rather than CSV --- cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll | 2 +- .../lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll | 2 +- go/ql/lib/semmle/go/dataflow/ExternalFlow.qll | 2 +- java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll index b36c37d8114..df1765ec07c 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll @@ -3,7 +3,7 @@ * * Provides classes and predicates for dealing with flow models specified in CSV format. * - * The CSV specification has the following columns: + * The extensible relations have the following columns: * - Sources: * `namespace; type; subtypes; name; signature; ext; output; kind` * - Sinks: diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll index 2b4264fc432..95b9578e4f3 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll @@ -4,7 +4,7 @@ * Provides classes and predicates for dealing with MaD flow models specified * in data extensions and CSV format. * - * The CSV specification has the following columns: + * The extensible relations have the following columns: * - Sources: * `namespace; type; subtypes; name; signature; ext; output; kind; provenance` * - Sinks: diff --git a/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll b/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll index 23e08ce5cbf..05379c620fb 100644 --- a/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll +++ b/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll @@ -4,7 +4,7 @@ * Provides classes and predicates for dealing with flow models specified * in data extensions and CSV format. * - * The CSV specification has the following columns: + * The extensible relations have the following columns: * - Sources: * `package; type; subtypes; name; signature; ext; output; kind; provenance` * - Sinks: diff --git a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll index 45db15897f7..8f6d1a7855a 100644 --- a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll @@ -4,7 +4,7 @@ * Provides classes and predicates for dealing with flow models specified * in data extensions and CSV format. * - * The CSV specification has the following columns: + * The extensible relations have the following columns: * - Sources: * `package; type; subtypes; name; signature; ext; output; kind; provenance` * - Sinks: From 886a16bfad664c67da64f4136a3079009a38bd11 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 26 Mar 2026 12:09:11 +0000 Subject: [PATCH 08/49] C++: Add provenance column --- cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll index df1765ec07c..ed40d391917 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll @@ -5,11 +5,11 @@ * * The extensible relations have the following columns: * - Sources: - * `namespace; type; subtypes; name; signature; ext; output; kind` + * `namespace; type; subtypes; name; signature; ext; output; kind; provenance` * - Sinks: - * `namespace; type; subtypes; name; signature; ext; input; kind` + * `namespace; type; subtypes; name; signature; ext; input; kind; provenance` * - Summaries: - * `namespace; type; subtypes; name; signature; ext; input; output; kind` + * `namespace; type; subtypes; name; signature; ext; input; output; kind; provenance` * - Barriers: * `namespace; type; subtypes; name; signature; ext; output; kind; provenance` * - BarrierGuards: From 5451424e751d102f492d4f7298518afd20cdf6e6 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 27 Mar 2026 09:46:20 +0000 Subject: [PATCH 09/49] Rust: Fix columns for neutrals --- rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll b/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll index a43495ac784..cc7dd9963ea 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll @@ -14,7 +14,7 @@ * - BarrierGuards: * `path; input; branch; kind; provenance` * - Neutrals: - * `package; type; name; signature; kind; provenance` + * `path; kind; provenance` * A neutral is used to indicate that a callable is neutral with respect to flow (no summary), source (is not a source) or sink (is not a sink). * * The interpretation of a row is similar to API-graphs with a left-to-right From b3285c6ae2274aaa456a2fe152945f38bd16f8af Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 27 Mar 2026 11:35:22 +0000 Subject: [PATCH 10/49] Make description of `acceptingvalue` column clearer --- cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll | 5 ++--- .../semmle/code/csharp/dataflow/internal/ExternalFlow.qll | 4 ++-- go/ql/lib/semmle/go/dataflow/ExternalFlow.qll | 5 ++--- java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll | 4 ++-- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll index ed40d391917..3fe9f6aaedf 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll @@ -91,9 +91,8 @@ * - flow from the _second_ indirection of the 0th argument to the first * indirection of the return value, etc. * 8. The `acceptingvalue` column of barrier guard models specifies the condition - * under which the guard accepts or blocks flow. It can be one of "true" or - * "false". In the future "no-exception", "not-zero", "null", "not-null" may be - * supported. + * under which the guard blocks flow. It can be one of "true" or "false". In + * the future "no-exception", "not-zero", "null", "not-null" may be supported. * 9. The `kind` column is a tag that can be referenced from QL to determine to * which classes the interpreted elements should be added. For example, for * sources "remote" indicates a default remote flow source, and for summaries diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll index 95b9578e4f3..17cdcc1bf0b 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll @@ -74,8 +74,8 @@ * - "Property[p]": Selects the contents of property `p`. * * 8. The `acceptingvalue` column of barrier guard models specifies the condition - * under which the guard accepts or blocks flow. It can be one of "true" or - * "false", "no-exception", "not-zero", "null", "not-null". + * under which the guard blocks flow. It can be one of "true" or "false". In + * the future "no-exception", "not-zero", "null", "not-null" may be supported. * 9. The `kind` column is a tag that can be referenced from QL to determine to * which classes the interpreted elements should be added. For example, for * sources "remote" indicates a default remote flow source, and for summaries diff --git a/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll b/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll index 05379c620fb..0ad28bac533 100644 --- a/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll +++ b/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll @@ -83,9 +83,8 @@ * - "Dereference": Selects the value referenced by a pointer. * * 8. The `acceptingvalue` column of barrier guard models specifies the condition - * under which the guard accepts or blocks flow. It can be one of "true" or - * "false". In the future "no-exception", "not-zero", "null", "not-null" may be - * supported. + * under which the guard blocks flow. It can be one of "true" or "false". In + * the future "no-exception", "not-zero", "null", "not-null" may be supported. * 9. The `kind` column is a tag that can be referenced from QL to determine to * which classes the interpreted elements should be added. For example, for * sources "remote" indicates a default remote flow source, and for summaries diff --git a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll index 8f6d1a7855a..6ad4a5938a3 100644 --- a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll @@ -74,8 +74,8 @@ * - "ReturnValue": Selects the return value of a call to the selected element. * - "Element": Selects the collection elements of the selected element. * 8. The `acceptingvalue` column of barrier guard models specifies the condition - * under which the guard accepts or blocks flow. It can be one of "true" or - * "false", "no-exception", "not-zero", "null", "not-null". + * under which the guard blocks flow. It can be one of "true" or "false". In + * the future "no-exception", "not-zero", "null", "not-null" may be supported. * 9. The `kind` column is a tag that can be referenced from QL to determine to * which classes the interpreted elements should be added. For example, for * sources "remote" indicates a default remote flow source, and for summaries From 60f9ce4ce7485269027caefacd2fa5ee73099d90 Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 20 Mar 2026 13:55:58 +0000 Subject: [PATCH 11/49] Python: Port UnreachableCode.ql --- python/ql/src/Statements/UnreachableCode.ql | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/python/ql/src/Statements/UnreachableCode.ql b/python/ql/src/Statements/UnreachableCode.ql index 55582ed2f06..200e073cff6 100644 --- a/python/ql/src/Statements/UnreachableCode.ql +++ b/python/ql/src/Statements/UnreachableCode.ql @@ -13,7 +13,7 @@ */ import python -private import LegacyPointsTo +private import semmle.python.ApiGraphs predicate typing_import(ImportingStmt is) { exists(Module m | @@ -34,11 +34,7 @@ predicate unique_yield(Stmt s) { /** Holds if `contextlib.suppress` may be used in the same scope as `s` */ predicate suppression_in_scope(Stmt s) { exists(With w | - w.getContextExpr() - .(Call) - .getFunc() - .(ExprWithPointsTo) - .pointsTo(Value::named("contextlib.suppress")) and + w.getContextExpr() = API::moduleImport("contextlib").getMember("suppress").getACall().asExpr() and w.getScope() = s.getScope() ) } From 0ea80ac184013820dbd2da2e5b4ba24c8ea92f3d Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 20 Feb 2026 15:03:15 +0000 Subject: [PATCH 12/49] Python: Port UnusedExceptionObject.ql Depending on whether other queries depend on this, we may end up moving the exception utility functions to a more central location. --- .../src/Statements/UnusedExceptionObject.ql | 46 +++++++++++++++++-- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/python/ql/src/Statements/UnusedExceptionObject.ql b/python/ql/src/Statements/UnusedExceptionObject.ql index 9a6a3650b7e..890cdc963ac 100644 --- a/python/ql/src/Statements/UnusedExceptionObject.ql +++ b/python/ql/src/Statements/UnusedExceptionObject.ql @@ -12,11 +12,49 @@ */ import python -private import LegacyPointsTo +private import semmle.python.dataflow.new.internal.DataFlowDispatch +private import semmle.python.dataflow.new.internal.Builtins +private import semmle.python.ApiGraphs -from Call call, ClassValue ex +/** + * Holds if `cls` is a user-defined exception class, i.e. it transitively + * extends one of the builtin exception base classes. + */ +predicate isUserDefinedExceptionClass(Class cls) { + cls.getABase() = + API::builtin(["BaseException", "Exception"]).getAValueReachableFromSource().asExpr() + or + isUserDefinedExceptionClass(getADirectSuperclass(cls)) +} + +/** + * Gets the name of a builtin exception class. + */ +string getBuiltinExceptionName() { + result = Builtins::getBuiltinName() and + ( + result.matches("%Error") or + result.matches("%Exception") or + result.matches("%Warning") or + result = + ["GeneratorExit", "KeyboardInterrupt", "StopIteration", "StopAsyncIteration", "SystemExit"] + ) +} + +/** + * Holds if `call` is an instantiation of an exception class. + */ +predicate isExceptionInstantiation(Call call) { + exists(Class cls | + classTracker(cls).asExpr() = call.getFunc() and + isUserDefinedExceptionClass(cls) + ) + or + call.getFunc() = API::builtin(getBuiltinExceptionName()).getAValueReachableFromSource().asExpr() +} + +from Call call where - call.getFunc().(ExprWithPointsTo).pointsTo(ex) and - ex.getASuperType() = ClassValue::exception() and + isExceptionInstantiation(call) and exists(ExprStmt s | s.getValue() = call) select call, "Instantiating an exception, but not raising it, has no effect." From bb9873dc8fff136f5e119400ca9cc67f08d0cdb2 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 27 Mar 2026 16:40:45 +0000 Subject: [PATCH 13/49] C++: Increase the query precision to high. --- .../Underspecified Functions/ImplicitFunctionDeclaration.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql b/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql index 007ef71a163..0cf6c8b3714 100644 --- a/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql +++ b/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql @@ -5,7 +5,7 @@ * may lead to unpredictable behavior. * @kind problem * @problem.severity warning - * @precision medium + * @precision high * @id cpp/implicit-function-declaration * @tags correctness * maintainability From 50681a3c42fb901cd231e0988c1f3047acbff075 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 27 Mar 2026 16:47:31 +0000 Subject: [PATCH 14/49] C++: Add note to the .qhelp. --- .../ImplicitFunctionDeclaration.qhelp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.qhelp b/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.qhelp index 6ff60d38341..d9b5a022077 100644 --- a/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.qhelp +++ b/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.qhelp @@ -14,6 +14,9 @@ function may behave unpredictably.

This may indicate a misspelled function name, or that the required header containing the function declaration has not been included.

+

Note: This query is not compatible with build mode: none databases, and produces +no results on those databases.

+

Provide an explicit declaration of the function before invoking it.

@@ -26,4 +29,4 @@ the function declaration has not been included.

  • SEI CERT C Coding Standard: DCL31-C. Declare identifiers before using them
  • - \ No newline at end of file + From 4f3108c444156d5a072c19845f7083690a0342b0 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 27 Mar 2026 17:04:05 +0000 Subject: [PATCH 15/49] C++: Update change note. --- .../src/change-notes/2026-03-23-implicit-function-declaration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/ql/src/change-notes/2026-03-23-implicit-function-declaration.md b/cpp/ql/src/change-notes/2026-03-23-implicit-function-declaration.md index 8c2c431ec24..4fc4808f40c 100644 --- a/cpp/ql/src/change-notes/2026-03-23-implicit-function-declaration.md +++ b/cpp/ql/src/change-notes/2026-03-23-implicit-function-declaration.md @@ -1,4 +1,5 @@ --- category: minorAnalysis --- +* The "Implicit function declaration" (`cpp/implicit-function-declaration`) query has been promoted to `@precision high`. * The "Implicit function declaration" (`cpp/implicit-function-declaration`) query no longer produces results on `build mode: none` databases. These results were found to be very noisy and fundamentally imprecise in this mode. From a9cce1c0fa75b167c549b46ab350d7970929750a Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Fri, 27 Mar 2026 17:32:03 +0000 Subject: [PATCH 16/49] C++: Undo increasing query precision. --- .../Underspecified Functions/ImplicitFunctionDeclaration.ql | 2 +- .../change-notes/2026-03-23-implicit-function-declaration.md | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql b/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql index 0cf6c8b3714..007ef71a163 100644 --- a/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql +++ b/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql @@ -5,7 +5,7 @@ * may lead to unpredictable behavior. * @kind problem * @problem.severity warning - * @precision high + * @precision medium * @id cpp/implicit-function-declaration * @tags correctness * maintainability diff --git a/cpp/ql/src/change-notes/2026-03-23-implicit-function-declaration.md b/cpp/ql/src/change-notes/2026-03-23-implicit-function-declaration.md index 4fc4808f40c..8c2c431ec24 100644 --- a/cpp/ql/src/change-notes/2026-03-23-implicit-function-declaration.md +++ b/cpp/ql/src/change-notes/2026-03-23-implicit-function-declaration.md @@ -1,5 +1,4 @@ --- category: minorAnalysis --- -* The "Implicit function declaration" (`cpp/implicit-function-declaration`) query has been promoted to `@precision high`. * The "Implicit function declaration" (`cpp/implicit-function-declaration`) query no longer produces results on `build mode: none` databases. These results were found to be very noisy and fundamentally imprecise in this mode. From a7fdc4b5435e38cffb68fa425cbdec62939091bb Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 27 Mar 2026 22:15:45 +0000 Subject: [PATCH 17/49] Replace `acceptingvalue` with `acceptingValue` --- .../semmle/code/cpp/dataflow/ExternalFlow.qll | 16 +++++++-------- .../internal/ExternalFlowExtensions.qll | 2 +- .../cpp/dataflow/internal/FlowSummaryImpl.qll | 4 ++-- .../csharp/dataflow/internal/ExternalFlow.qll | 18 ++++++++--------- .../internal/ExternalFlowExtensions.qll | 2 +- .../dataflow/internal/FlowSummaryImpl.qll | 4 ++-- go/ql/lib/semmle/go/dataflow/ExternalFlow.qll | 18 ++++++++--------- .../internal/ExternalFlowExtensions.qll | 2 +- .../go/dataflow/internal/FlowSummaryImpl.qll | 4 ++-- .../code/java/dataflow/ExternalFlow.qll | 18 ++++++++--------- .../internal/ExternalFlowExtensions.qll | 2 +- .../dataflow/internal/FlowSummaryImpl.qll | 4 ++-- .../rust/dataflow/internal/DataFlowImpl.qll | 6 +++--- .../dataflow/internal/FlowSummaryImpl.qll | 20 +++++++++---------- shared/mad/codeql/mad/static/ModelsAsData.qll | 12 +++++------ .../dataflow/internal/FlowSummaryImpl.qll | 2 +- 16 files changed, 67 insertions(+), 67 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll index 3fe9f6aaedf..e97b6d044d7 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll @@ -13,7 +13,7 @@ * - Barriers: * `namespace; type; subtypes; name; signature; ext; output; kind; provenance` * - BarrierGuards: - * `namespace; type; subtypes; name; signature; ext; input; acceptingvalue; kind; provenance` + * `namespace; type; subtypes; name; signature; ext; input; acceptingValue; kind; provenance` * * The interpretation of a row is similar to API-graphs with a left-to-right * reading. @@ -90,7 +90,7 @@ * value, and * - flow from the _second_ indirection of the 0th argument to the first * indirection of the return value, etc. - * 8. The `acceptingvalue` column of barrier guard models specifies the condition + * 8. The `acceptingValue` column of barrier guard models specifies the condition * under which the guard blocks flow. It can be one of "true" or "false". In * the future "no-exception", "not-zero", "null", "not-null" may be supported. * 9. The `kind` column is a tag that can be referenced from QL to determine to @@ -1089,13 +1089,13 @@ private module Cached { private predicate barrierGuardChecks(IRGuardCondition g, Expr e, boolean gv, TKindModelPair kmp) { exists( - SourceSinkInterpretationInput::InterpretNode n, Public::AcceptingValue acceptingvalue, + SourceSinkInterpretationInput::InterpretNode n, Public::AcceptingValue acceptingValue, string kind, string model | - isBarrierGuardNode(n, acceptingvalue, kind, model) and + isBarrierGuardNode(n, acceptingValue, kind, model) and n.asNode().asExpr() = e and kmp = TMkPair(kind, model) and - gv = convertAcceptingValue(acceptingvalue).asBooleanValue() and + gv = convertAcceptingValue(acceptingValue).asBooleanValue() and n.asNode().(Private::ArgumentNode).getCall().asCallInstruction() = g ) } @@ -1112,14 +1112,14 @@ private module Cached { ) { exists( SourceSinkInterpretationInput::InterpretNode interpretNode, - Public::AcceptingValue acceptingvalue, string kind, string model, int indirectionIndex, + Public::AcceptingValue acceptingValue, string kind, string model, int indirectionIndex, Private::ArgumentNode arg | - isBarrierGuardNode(interpretNode, acceptingvalue, kind, model) and + isBarrierGuardNode(interpretNode, acceptingValue, kind, model) and arg = interpretNode.asNode() and arg.asIndirectExpr(indirectionIndex) = e and kmp = MkKindModelPairIntPair(TMkPair(kind, model), indirectionIndex) and - gv = convertAcceptingValue(acceptingvalue).asBooleanValue() and + gv = convertAcceptingValue(acceptingValue).asBooleanValue() and arg.getCall().asCallInstruction() = g ) } diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/ExternalFlowExtensions.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/ExternalFlowExtensions.qll index 1a572c221d9..22c74c2aa71 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/ExternalFlowExtensions.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/ExternalFlowExtensions.qll @@ -33,7 +33,7 @@ extensible predicate barrierModel( */ extensible predicate barrierGuardModel( string namespace, string type, boolean subtypes, string name, string signature, string ext, - string input, string acceptingvalue, string kind, string provenance, QlBuiltins::ExtensionId madId + string input, string acceptingValue, string kind, string provenance, QlBuiltins::ExtensionId madId ); /** diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll index cce1b80e7fc..d91dc41febe 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll @@ -162,13 +162,13 @@ module SourceSinkInterpretationInput implements } predicate barrierGuardElement( - Element e, string input, Public::AcceptingValue acceptingvalue, string kind, + Element e, string input, Public::AcceptingValue acceptingValue, string kind, Public::Provenance provenance, string model ) { exists( string package, string type, boolean subtypes, string name, string signature, string ext | - barrierGuardModel(package, type, subtypes, name, signature, ext, input, acceptingvalue, kind, + barrierGuardModel(package, type, subtypes, name, signature, ext, input, acceptingValue, kind, provenance, model) and e = interpretElement(package, type, subtypes, name, signature, ext) ) diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll index 17cdcc1bf0b..f8cec8c4d9f 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll @@ -14,7 +14,7 @@ * - Barriers: * `namespace; type; subtypes; name; signature; ext; output; kind; provenance` * - BarrierGuards: - * `namespace; type; subtypes; name; signature; ext; input; acceptingvalue; kind; provenance` + * `namespace; type; subtypes; name; signature; ext; input; acceptingValue; kind; provenance` * - Neutrals: * `namespace; type; name; signature; kind; provenance` * A neutral is used to indicate that a callable is neutral with respect to flow (no summary), source (is not a source) or sink (is not a sink). @@ -73,7 +73,7 @@ * - "Field[f]": Selects the contents of field `f`. * - "Property[p]": Selects the contents of property `p`. * - * 8. The `acceptingvalue` column of barrier guard models specifies the condition + * 8. The `acceptingValue` column of barrier guard models specifies the condition * under which the guard blocks flow. It can be one of "true" or "false". In * the future "no-exception", "not-zero", "null", "not-null" may be supported. * 9. The `kind` column is a tag that can be referenced from QL to determine to @@ -237,11 +237,11 @@ module ModelValidation { result = "Unrecognized provenance description \"" + provenance + "\" in " + pred + " model." ) or - exists(string acceptingvalue | - barrierGuardModel(_, _, _, _, _, _, _, acceptingvalue, _, _, _) and - invalidAcceptingValue(acceptingvalue) and + exists(string acceptingValue | + barrierGuardModel(_, _, _, _, _, _, _, acceptingValue, _, _, _) and + invalidAcceptingValue(acceptingValue) and result = - "Unrecognized accepting value description \"" + acceptingvalue + + "Unrecognized accepting value description \"" + acceptingValue + "\" in barrier guard model." ) } @@ -489,13 +489,13 @@ private module Cached { private predicate barrierGuardChecks(Guard g, Expr e, GuardValue gv, TKindModelPair kmp) { exists( - SourceSinkInterpretationInput::InterpretNode n, AcceptingValue acceptingvalue, string kind, + SourceSinkInterpretationInput::InterpretNode n, AcceptingValue acceptingValue, string kind, string model | - isBarrierGuardNode(n, acceptingvalue, kind, model) and + isBarrierGuardNode(n, acceptingValue, kind, model) and n.asNode().asExpr() = e and kmp = TMkPair(kind, model) and - gv = convertAcceptingValue(acceptingvalue) + gv = convertAcceptingValue(acceptingValue) | g.(Call).getAnArgument() = e or g.(QualifiableExpr).getQualifier() = e ) diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlowExtensions.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlowExtensions.qll index 3461f0a5186..cd438ece284 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlowExtensions.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlowExtensions.qll @@ -33,7 +33,7 @@ extensible predicate barrierModel( */ extensible predicate barrierGuardModel( string namespace, string type, boolean subtypes, string name, string signature, string ext, - string input, string acceptingvalue, string kind, string provenance, QlBuiltins::ExtensionId madId + string input, string acceptingValue, string kind, string provenance, QlBuiltins::ExtensionId madId ); /** diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll index 6f9b621ff40..4b79ed5feca 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll @@ -253,13 +253,13 @@ module SourceSinkInterpretationInput implements } predicate barrierGuardElement( - Element e, string input, Public::AcceptingValue acceptingvalue, string kind, + Element e, string input, Public::AcceptingValue acceptingValue, string kind, Public::Provenance provenance, string model ) { exists( string namespace, string type, boolean subtypes, string name, string signature, string ext | - barrierGuardModel(namespace, type, subtypes, name, signature, ext, input, acceptingvalue, + barrierGuardModel(namespace, type, subtypes, name, signature, ext, input, acceptingValue, kind, provenance, model) and e = interpretElement(namespace, type, subtypes, name, signature, ext, _) ) diff --git a/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll b/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll index 0ad28bac533..f0dc0cf0ca2 100644 --- a/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll +++ b/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll @@ -14,7 +14,7 @@ * - Barriers: * `package; type; subtypes; name; signature; ext; output; kind; provenance` * - BarrierGuards: - * `package; type; subtypes; name; signature; ext; input; acceptingvalue; kind; provenance` + * `package; type; subtypes; name; signature; ext; input; acceptingValue; kind; provenance` * - Neutrals: * `package; type; name; signature; kind; provenance` * A neutral is used to indicate that a callable is neutral with respect to flow (no summary), source (is not a source) or sink (is not a sink). @@ -82,7 +82,7 @@ * - "MapValue": Selects a value in a map. * - "Dereference": Selects the value referenced by a pointer. * - * 8. The `acceptingvalue` column of barrier guard models specifies the condition + * 8. The `acceptingValue` column of barrier guard models specifies the condition * under which the guard blocks flow. It can be one of "true" or "false". In * the future "no-exception", "not-zero", "null", "not-null" may be supported. * 9. The `kind` column is a tag that can be referenced from QL to determine to @@ -266,11 +266,11 @@ module ModelValidation { result = "Unrecognized provenance description \"" + provenance + "\" in " + pred + " model." ) or - exists(string acceptingvalue | - barrierGuardModel(_, _, _, _, _, _, _, acceptingvalue, _, _, _) and - invalidAcceptingValue(acceptingvalue) and + exists(string acceptingValue | + barrierGuardModel(_, _, _, _, _, _, _, acceptingValue, _, _, _) and + invalidAcceptingValue(acceptingValue) and result = - "Unrecognized accepting value description \"" + acceptingvalue + + "Unrecognized accepting value description \"" + acceptingValue + "\" in barrier guard model." ) } @@ -478,13 +478,13 @@ private module Cached { private predicate barrierGuardChecks(DataFlow::Node g, Expr e, boolean gv, TKindModelPair kmp) { exists( - SourceSinkInterpretationInput::InterpretNode n, Public::AcceptingValue acceptingvalue, + SourceSinkInterpretationInput::InterpretNode n, Public::AcceptingValue acceptingValue, string kind, string model | - isBarrierGuardNode(n, acceptingvalue, kind, model) and + isBarrierGuardNode(n, acceptingValue, kind, model) and n.asNode().asExpr() = e and kmp = TMkPair(kind, model) and - gv = convertAcceptingValue(acceptingvalue) + gv = convertAcceptingValue(acceptingValue) | g.asExpr().(CallExpr).getAnArgument() = e // TODO: qualifier? ) diff --git a/go/ql/lib/semmle/go/dataflow/internal/ExternalFlowExtensions.qll b/go/ql/lib/semmle/go/dataflow/internal/ExternalFlowExtensions.qll index 5d43cf674c1..ab2a241e14a 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/ExternalFlowExtensions.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/ExternalFlowExtensions.qll @@ -35,7 +35,7 @@ extensible predicate barrierModel( */ extensible predicate barrierGuardModel( string package, string type, boolean subtypes, string name, string signature, string ext, - string input, string acceptingvalue, string kind, string provenance, QlBuiltins::ExtensionId madId + string input, string acceptingValue, string kind, string provenance, QlBuiltins::ExtensionId madId ); /** diff --git a/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll b/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll index 240665bd492..ff727286c3b 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll @@ -174,13 +174,13 @@ module SourceSinkInterpretationInput implements } predicate barrierGuardElement( - Element e, string input, Public::AcceptingValue acceptingvalue, string kind, + Element e, string input, Public::AcceptingValue acceptingValue, string kind, Public::Provenance provenance, string model ) { exists( string package, string type, boolean subtypes, string name, string signature, string ext | - barrierGuardModel(package, type, subtypes, name, signature, ext, input, acceptingvalue, kind, + barrierGuardModel(package, type, subtypes, name, signature, ext, input, acceptingValue, kind, provenance, model) and e = interpretElement(package, type, subtypes, name, signature, ext) ) diff --git a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll index 6ad4a5938a3..a6a9347ca03 100644 --- a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll @@ -14,7 +14,7 @@ * - Barriers: * `package; type; subtypes; name; signature; ext; output; kind; provenance` * - BarrierGuards: - * `package; type; subtypes; name; signature; ext; input; acceptingvalue; kind; provenance` + * `package; type; subtypes; name; signature; ext; input; acceptingValue; kind; provenance` * - Neutrals: * `package; type; name; signature; kind; provenance` * A neutral is used to indicate that a callable is neutral with respect to flow (no summary), source (is not a source) or sink (is not a sink). @@ -73,7 +73,7 @@ * in the given range. The range is inclusive at both ends. * - "ReturnValue": Selects the return value of a call to the selected element. * - "Element": Selects the collection elements of the selected element. - * 8. The `acceptingvalue` column of barrier guard models specifies the condition + * 8. The `acceptingValue` column of barrier guard models specifies the condition * under which the guard blocks flow. It can be one of "true" or "false". In * the future "no-exception", "not-zero", "null", "not-null" may be supported. * 9. The `kind` column is a tag that can be referenced from QL to determine to @@ -365,11 +365,11 @@ module ModelValidation { result = "Unrecognized provenance description \"" + provenance + "\" in " + pred + " model." ) or - exists(string acceptingvalue | - barrierGuardModel(_, _, _, _, _, _, _, acceptingvalue, _, _, _) and - invalidAcceptingValue(acceptingvalue) and + exists(string acceptingValue | + barrierGuardModel(_, _, _, _, _, _, _, acceptingValue, _, _, _) and + invalidAcceptingValue(acceptingValue) and result = - "Unrecognized accepting value description \"" + acceptingvalue + + "Unrecognized accepting value description \"" + acceptingValue + "\" in barrier guard model." ) } @@ -590,13 +590,13 @@ private module Cached { private predicate barrierGuardChecks(Guard g, Expr e, GuardValue gv, TKindModelPair kmp) { exists( - SourceSinkInterpretationInput::InterpretNode n, AcceptingValue acceptingvalue, string kind, + SourceSinkInterpretationInput::InterpretNode n, AcceptingValue acceptingValue, string kind, string model | - isBarrierGuardNode(n, acceptingvalue, kind, model) and + isBarrierGuardNode(n, acceptingValue, kind, model) and n.asNode().asExpr() = e and kmp = TMkPair(kind, model) and - gv = convertAcceptingValue(acceptingvalue) + gv = convertAcceptingValue(acceptingValue) | g.(Call).getAnArgument() = e or g.(MethodCall).getQualifier() = e ) diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/ExternalFlowExtensions.qll b/java/ql/lib/semmle/code/java/dataflow/internal/ExternalFlowExtensions.qll index be474ad4535..3c6b003876d 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/ExternalFlowExtensions.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/ExternalFlowExtensions.qll @@ -35,7 +35,7 @@ extensible predicate barrierModel( */ extensible predicate barrierGuardModel( string package, string type, boolean subtypes, string name, string signature, string ext, - string input, string acceptingvalue, string kind, string provenance, QlBuiltins::ExtensionId madId + string input, string acceptingValue, string kind, string provenance, QlBuiltins::ExtensionId madId ); /** diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll b/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll index 64fa30c7d91..453b7ccae11 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll @@ -282,7 +282,7 @@ module SourceSinkInterpretationInput implements } predicate barrierGuardElement( - Element e, string input, Public::AcceptingValue acceptingvalue, string kind, + Element e, string input, Public::AcceptingValue acceptingValue, string kind, Public::Provenance provenance, string model ) { exists( @@ -290,7 +290,7 @@ module SourceSinkInterpretationInput implements SourceOrSinkElement baseBarrier, string originalInput | barrierGuardModel(namespace, type, subtypes, name, signature, ext, originalInput, - acceptingvalue, kind, provenance, model) and + acceptingValue, kind, provenance, model) and baseBarrier = interpretElement(namespace, type, subtypes, name, signature, ext, _) and ( e = baseBarrier and input = originalInput diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll index 27773758fc4..7c1fdd8cf78 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll @@ -1183,12 +1183,12 @@ private module Cached { exists( FlowSummaryImpl::Public::BarrierGuardElement b, FlowSummaryImpl::Private::SummaryComponentStack stack, - FlowSummaryImpl::Public::AcceptingValue acceptingvalue, string kind, string model + FlowSummaryImpl::Public::AcceptingValue acceptingValue, string kind, string model | - FlowSummaryImpl::Private::barrierGuardSpec(b, stack, acceptingvalue, kind, model) and + FlowSummaryImpl::Private::barrierGuardSpec(b, stack, acceptingValue, kind, model) and e = FlowSummaryImpl::StepsInput::getSinkNode(b, stack.headOfSingleton()).asExpr() and kmp = TMkPair(kind, model) and - gv = convertAcceptingValue(acceptingvalue) and + gv = convertAcceptingValue(acceptingValue) and g = b.getCall() ) } diff --git a/shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll b/shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll index 8b25c54bfa0..0c6e42d9066 100644 --- a/shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll +++ b/shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll @@ -2189,10 +2189,10 @@ module Make< not exists(interpretComponent(c)) } - /** Holds if `acceptingvalue` is not a valid barrier guard accepting-value. */ - bindingset[acceptingvalue] - predicate invalidAcceptingValue(string acceptingvalue) { - not acceptingvalue instanceof AcceptingValue + /** Holds if `acceptingValue` is not a valid barrier guard accepting-value. */ + bindingset[acceptingValue] + predicate invalidAcceptingValue(string acceptingValue) { + not acceptingValue instanceof AcceptingValue } /** Holds if `provenance` is not a valid provenance value. */ @@ -2242,10 +2242,10 @@ module Make< /** * Holds if an external barrier guard specification exists for `n` with input - * specification `input`, accepting value `acceptingvalue`, and kind `kind`. + * specification `input`, accepting value `acceptingValue`, and kind `kind`. */ predicate barrierGuardElement( - Element n, string input, AcceptingValue acceptingvalue, string kind, + Element n, string input, AcceptingValue acceptingValue, string kind, Provenance provenance, string model ); @@ -2371,11 +2371,11 @@ module Make< } private predicate barrierGuardElementRef( - InterpretNode ref, SourceSinkAccessPath input, AcceptingValue acceptingvalue, string kind, + InterpretNode ref, SourceSinkAccessPath input, AcceptingValue acceptingValue, string kind, string model ) { exists(SourceOrSinkElement e | - barrierGuardElement(e, input, acceptingvalue, kind, _, model) and + barrierGuardElement(e, input, acceptingValue, kind, _, model) and if inputNeedsReferenceExt(input.getToken(0)) then e = ref.getCallTarget() else e = ref.asElement() @@ -2518,10 +2518,10 @@ module Make< * given kind in a MaD flow model. */ predicate isBarrierGuardNode( - InterpretNode node, AcceptingValue acceptingvalue, string kind, string model + InterpretNode node, AcceptingValue acceptingValue, string kind, string model ) { exists(InterpretNode ref, SourceSinkAccessPath input | - barrierGuardElementRef(ref, input, acceptingvalue, kind, model) and + barrierGuardElementRef(ref, input, acceptingValue, kind, model) and interpretInput(input, input.getNumToken(), ref, node) ) } diff --git a/shared/mad/codeql/mad/static/ModelsAsData.qll b/shared/mad/codeql/mad/static/ModelsAsData.qll index 84daaa9b6c8..4b58a23186a 100644 --- a/shared/mad/codeql/mad/static/ModelsAsData.qll +++ b/shared/mad/codeql/mad/static/ModelsAsData.qll @@ -31,7 +31,7 @@ signature module ExtensionsSig { */ predicate barrierGuardModel( string namespace, string type, boolean subtypes, string name, string signature, string ext, - string input, string acceptingvalue, string kind, string provenance, + string input, string acceptingValue, string kind, string provenance, QlBuiltins::ExtensionId madId ); @@ -142,14 +142,14 @@ module ModelsAsData { or exists( string namespace, string type, boolean subtypes, string name, string signature, string ext, - string input, string acceptingvalue, string kind, string provenance + string input, string acceptingValue, string kind, string provenance | Extensions::barrierGuardModel(namespace, type, subtypes, name, signature, ext, input, - acceptingvalue, kind, provenance, madId) + acceptingValue, kind, provenance, madId) | model = "Barrier Guard: " + namespace + "; " + type + "; " + subtypes + "; " + name + "; " + - signature + "; " + ext + "; " + input + "; " + acceptingvalue + "; " + kind + "; " + + signature + "; " + ext + "; " + input + "; " + acceptingValue + "; " + kind + "; " + provenance ) or @@ -241,12 +241,12 @@ module ModelsAsData { /** Holds if a barrier guard model exists for the given parameters. */ predicate barrierGuardModel( string namespace, string type, boolean subtypes, string name, string signature, string ext, - string input, string acceptingvalue, string kind, string provenance, string model + string input, string acceptingValue, string kind, string provenance, string model ) { exists(string namespaceOrGroup, QlBuiltins::ExtensionId madId | namespace = getNamespace(namespaceOrGroup) and Extensions::barrierGuardModel(namespaceOrGroup, type, subtypes, name, signature, ext, input, - acceptingvalue, kind, provenance, madId) and + acceptingValue, kind, provenance, madId) and model = "MaD:" + madId.toString() ) } diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImpl.qll b/swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImpl.qll index c1ddb7f781f..3a096fe3d57 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImpl.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImpl.qll @@ -168,7 +168,7 @@ module SourceSinkInterpretationInput implements } predicate barrierGuardElement( - Element n, string input, Public::AcceptingValue acceptingvalue, string kind, + Element n, string input, Public::AcceptingValue acceptingValue, string kind, Public::Provenance provenance, string model ) { none() From c5ef1f6342bbc04bf34f95abc397df1089617d2c Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 20 Mar 2026 13:56:07 +0000 Subject: [PATCH 18/49] Python: Port UseOfExit.ql --- python/ql/src/Statements/UseOfExit.ql | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/python/ql/src/Statements/UseOfExit.ql b/python/ql/src/Statements/UseOfExit.ql index 437ff93b537..2310a839f67 100644 --- a/python/ql/src/Statements/UseOfExit.ql +++ b/python/ql/src/Statements/UseOfExit.ql @@ -12,10 +12,12 @@ */ import python -private import LegacyPointsTo +private import semmle.python.ApiGraphs from CallNode call, string name -where call.getFunction().(ControlFlowNodeWithPointsTo).pointsTo(Value::siteQuitter(name)) +where + name = ["exit", "quit"] and + call = API::builtin(name).getACall().asCfgNode() select call, "The '" + name + "' site.Quitter object may not exist if the 'site' module is not loaded or is modified." From 37aac059640e804b4b7550279d5f2bfa1812c211 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 27 Mar 2026 22:39:10 +0000 Subject: [PATCH 19/49] Replace `branch` with `acceptingValue` --- .../data/internal/ApiGraphModels.qll | 26 +++++++++---------- .../internal/ApiGraphModelsExtensions.qll | 6 ++--- .../data/internal/ApiGraphModels.qll | 26 +++++++++---------- .../internal/ApiGraphModelsExtensions.qll | 6 ++--- .../data/internal/ApiGraphModels.qll | 26 +++++++++---------- .../internal/ApiGraphModelsExtensions.qll | 6 ++--- .../rust/dataflow/internal/ModelsAsData.qll | 21 ++++++++------- .../dataflow/internal/FlowSummaryImpl.qll | 16 ++++++------ 8 files changed, 67 insertions(+), 66 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll index 34bf3267522..155fb4b7c78 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll @@ -13,7 +13,7 @@ * - Barriers: * `type, path, kind` * - BarrierGuards: - * `type, path, branch, kind` + * `type, path, acceptingValue, kind` * - Types: * `type1, type2, path` * @@ -46,7 +46,7 @@ * 3. The `input` and `output` columns specify how data enters and leaves the element selected by the * first `(type, path)` tuple. Both strings are `.`-separated access paths * of the same syntax as the `path` column. - * 4. The `branch` column of barrier guard models specifies which branch of the guard is blocking flow. It can be "true" or "false". + * 4. The `acceptingValue` column of barrier guard models specifies which branch of the guard is blocking flow. It can be "true" or "false". * 5. The `kind` column is a tag that can be referenced from QL to determine to * which classes the interpreted elements should be added. For example, for * sources `"remote"` indicates a default remote flow source, and for summaries @@ -360,11 +360,11 @@ private predicate barrierModel(string type, string path, string kind, string mod /** Holds if a barrier guard model exists for the given parameters. */ private predicate barrierGuardModel( - string type, string path, string branch, string kind, string model + string type, string path, string acceptingValue, string kind, string model ) { // No deprecation adapter for barrier models, they were not around back then. exists(QlBuiltins::ExtensionId madId | - Extensions::barrierGuardModel(type, path, branch, kind, madId) and + Extensions::barrierGuardModel(type, path, acceptingValue, kind, madId) and model = "MaD:" + madId.toString() ) } @@ -788,16 +788,16 @@ module ModelOutput { } /** - * Holds if a barrier model contributed `barrier` with the given `kind` for the given `branch`. + * Holds if a barrier model contributed `barrier` with the given `kind` for the given `acceptingValue`. */ cached - API::Node getABarrierGuardNode(string kind, boolean branch, string model) { - exists(string type, string path, string branch_str | - branch = true and branch_str = "true" + API::Node getABarrierGuardNode(string kind, boolean acceptingValue, string model) { + exists(string type, string path, string acceptingValue_str | + acceptingValue = true and acceptingValue_str = "true" or - branch = false and branch_str = "false" + acceptingValue = false and acceptingValue_str = "false" | - barrierGuardModel(type, path, branch_str, kind, model) and + barrierGuardModel(type, path, acceptingValue_str, kind, model) and result = getNodeFromPath(type, path) ) } @@ -861,12 +861,12 @@ module ModelOutput { API::Node getABarrierNode(string kind) { result = getABarrierNode(kind, _) } /** - * Holds if an external model contributed `barrier-guard` with the given `kind` and `branch`. + * Holds if an external model contributed `barrier-guard` with the given `kind` and `acceptingValue`. * * INTERNAL: Do not use. */ - API::Node getABarrierGuardNode(string kind, boolean branch) { - result = getABarrierGuardNode(kind, branch, _) + API::Node getABarrierGuardNode(string kind, boolean acceptingValue) { + result = getABarrierGuardNode(kind, acceptingValue, _) } /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsExtensions.qll b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsExtensions.qll index 2a644aabb95..8d8a4f5fd88 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsExtensions.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsExtensions.qll @@ -33,11 +33,11 @@ extensible predicate barrierModel( * of the given `kind` and `madId` is the data extension row number. * `path` is assumed to lead to a parameter of a call (possibly `self`), and * the call is guarding the parameter. - * `branch` is either `true` or `false`, indicating which branch of the guard - * is protecting the parameter. + * `acceptingValue` is either `true` or `false`, indicating which branch of + * the guard is protecting the parameter. */ extensible predicate barrierGuardModel( - string type, string path, string branch, string kind, QlBuiltins::ExtensionId madId + string type, string path, string acceptingValue, string kind, QlBuiltins::ExtensionId madId ); /** diff --git a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll index 34bf3267522..155fb4b7c78 100644 --- a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll +++ b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll @@ -13,7 +13,7 @@ * - Barriers: * `type, path, kind` * - BarrierGuards: - * `type, path, branch, kind` + * `type, path, acceptingValue, kind` * - Types: * `type1, type2, path` * @@ -46,7 +46,7 @@ * 3. The `input` and `output` columns specify how data enters and leaves the element selected by the * first `(type, path)` tuple. Both strings are `.`-separated access paths * of the same syntax as the `path` column. - * 4. The `branch` column of barrier guard models specifies which branch of the guard is blocking flow. It can be "true" or "false". + * 4. The `acceptingValue` column of barrier guard models specifies which branch of the guard is blocking flow. It can be "true" or "false". * 5. The `kind` column is a tag that can be referenced from QL to determine to * which classes the interpreted elements should be added. For example, for * sources `"remote"` indicates a default remote flow source, and for summaries @@ -360,11 +360,11 @@ private predicate barrierModel(string type, string path, string kind, string mod /** Holds if a barrier guard model exists for the given parameters. */ private predicate barrierGuardModel( - string type, string path, string branch, string kind, string model + string type, string path, string acceptingValue, string kind, string model ) { // No deprecation adapter for barrier models, they were not around back then. exists(QlBuiltins::ExtensionId madId | - Extensions::barrierGuardModel(type, path, branch, kind, madId) and + Extensions::barrierGuardModel(type, path, acceptingValue, kind, madId) and model = "MaD:" + madId.toString() ) } @@ -788,16 +788,16 @@ module ModelOutput { } /** - * Holds if a barrier model contributed `barrier` with the given `kind` for the given `branch`. + * Holds if a barrier model contributed `barrier` with the given `kind` for the given `acceptingValue`. */ cached - API::Node getABarrierGuardNode(string kind, boolean branch, string model) { - exists(string type, string path, string branch_str | - branch = true and branch_str = "true" + API::Node getABarrierGuardNode(string kind, boolean acceptingValue, string model) { + exists(string type, string path, string acceptingValue_str | + acceptingValue = true and acceptingValue_str = "true" or - branch = false and branch_str = "false" + acceptingValue = false and acceptingValue_str = "false" | - barrierGuardModel(type, path, branch_str, kind, model) and + barrierGuardModel(type, path, acceptingValue_str, kind, model) and result = getNodeFromPath(type, path) ) } @@ -861,12 +861,12 @@ module ModelOutput { API::Node getABarrierNode(string kind) { result = getABarrierNode(kind, _) } /** - * Holds if an external model contributed `barrier-guard` with the given `kind` and `branch`. + * Holds if an external model contributed `barrier-guard` with the given `kind` and `acceptingValue`. * * INTERNAL: Do not use. */ - API::Node getABarrierGuardNode(string kind, boolean branch) { - result = getABarrierGuardNode(kind, branch, _) + API::Node getABarrierGuardNode(string kind, boolean acceptingValue) { + result = getABarrierGuardNode(kind, acceptingValue, _) } /** diff --git a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsExtensions.qll b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsExtensions.qll index 2a644aabb95..8d8a4f5fd88 100644 --- a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsExtensions.qll +++ b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsExtensions.qll @@ -33,11 +33,11 @@ extensible predicate barrierModel( * of the given `kind` and `madId` is the data extension row number. * `path` is assumed to lead to a parameter of a call (possibly `self`), and * the call is guarding the parameter. - * `branch` is either `true` or `false`, indicating which branch of the guard - * is protecting the parameter. + * `acceptingValue` is either `true` or `false`, indicating which branch of + * the guard is protecting the parameter. */ extensible predicate barrierGuardModel( - string type, string path, string branch, string kind, QlBuiltins::ExtensionId madId + string type, string path, string acceptingValue, string kind, QlBuiltins::ExtensionId madId ); /** diff --git a/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll b/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll index 34bf3267522..155fb4b7c78 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll @@ -13,7 +13,7 @@ * - Barriers: * `type, path, kind` * - BarrierGuards: - * `type, path, branch, kind` + * `type, path, acceptingValue, kind` * - Types: * `type1, type2, path` * @@ -46,7 +46,7 @@ * 3. The `input` and `output` columns specify how data enters and leaves the element selected by the * first `(type, path)` tuple. Both strings are `.`-separated access paths * of the same syntax as the `path` column. - * 4. The `branch` column of barrier guard models specifies which branch of the guard is blocking flow. It can be "true" or "false". + * 4. The `acceptingValue` column of barrier guard models specifies which branch of the guard is blocking flow. It can be "true" or "false". * 5. The `kind` column is a tag that can be referenced from QL to determine to * which classes the interpreted elements should be added. For example, for * sources `"remote"` indicates a default remote flow source, and for summaries @@ -360,11 +360,11 @@ private predicate barrierModel(string type, string path, string kind, string mod /** Holds if a barrier guard model exists for the given parameters. */ private predicate barrierGuardModel( - string type, string path, string branch, string kind, string model + string type, string path, string acceptingValue, string kind, string model ) { // No deprecation adapter for barrier models, they were not around back then. exists(QlBuiltins::ExtensionId madId | - Extensions::barrierGuardModel(type, path, branch, kind, madId) and + Extensions::barrierGuardModel(type, path, acceptingValue, kind, madId) and model = "MaD:" + madId.toString() ) } @@ -788,16 +788,16 @@ module ModelOutput { } /** - * Holds if a barrier model contributed `barrier` with the given `kind` for the given `branch`. + * Holds if a barrier model contributed `barrier` with the given `kind` for the given `acceptingValue`. */ cached - API::Node getABarrierGuardNode(string kind, boolean branch, string model) { - exists(string type, string path, string branch_str | - branch = true and branch_str = "true" + API::Node getABarrierGuardNode(string kind, boolean acceptingValue, string model) { + exists(string type, string path, string acceptingValue_str | + acceptingValue = true and acceptingValue_str = "true" or - branch = false and branch_str = "false" + acceptingValue = false and acceptingValue_str = "false" | - barrierGuardModel(type, path, branch_str, kind, model) and + barrierGuardModel(type, path, acceptingValue_str, kind, model) and result = getNodeFromPath(type, path) ) } @@ -861,12 +861,12 @@ module ModelOutput { API::Node getABarrierNode(string kind) { result = getABarrierNode(kind, _) } /** - * Holds if an external model contributed `barrier-guard` with the given `kind` and `branch`. + * Holds if an external model contributed `barrier-guard` with the given `kind` and `acceptingValue`. * * INTERNAL: Do not use. */ - API::Node getABarrierGuardNode(string kind, boolean branch) { - result = getABarrierGuardNode(kind, branch, _) + API::Node getABarrierGuardNode(string kind, boolean acceptingValue) { + result = getABarrierGuardNode(kind, acceptingValue, _) } /** diff --git a/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsExtensions.qll b/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsExtensions.qll index 2a644aabb95..8d8a4f5fd88 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsExtensions.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsExtensions.qll @@ -33,11 +33,11 @@ extensible predicate barrierModel( * of the given `kind` and `madId` is the data extension row number. * `path` is assumed to lead to a parameter of a call (possibly `self`), and * the call is guarding the parameter. - * `branch` is either `true` or `false`, indicating which branch of the guard - * is protecting the parameter. + * `acceptingValue` is either `true` or `false`, indicating which branch of + * the guard is protecting the parameter. */ extensible predicate barrierGuardModel( - string type, string path, string branch, string kind, QlBuiltins::ExtensionId madId + string type, string path, string acceptingValue, string kind, QlBuiltins::ExtensionId madId ); /** diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll b/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll index cc7dd9963ea..2b3ecf51fe4 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/ModelsAsData.qll @@ -12,7 +12,7 @@ * - Barriers: * `path; output; kind; provenance` * - BarrierGuards: - * `path; input; branch; kind; provenance` + * `path; input; acceptingValue; kind; provenance` * - Neutrals: * `path; kind; provenance` * A neutral is used to indicate that a callable is neutral with respect to flow (no summary), source (is not a source) or sink (is not a sink). @@ -41,7 +41,7 @@ * - `Field[i]`: the `i`th element of a tuple. * - `Reference`: the referenced value. * - `Future`: the value being computed asynchronously. - * 3. The `branch` column of barrier guard models specifies which branch of the + * 3. The `acceptingValue` column of barrier guard models specifies which branch of the * guard is blocking flow. It can be "true" or "false". In the future * "no-exception", "not-zero", "null", "not-null" may be supported. * 4. The `kind` column is a tag that can be referenced from QL to determine to @@ -124,11 +124,12 @@ extensible predicate barrierModel( * extension row number. * * The value referred to by `input` is assumed to lead to an argument of a call - * (possibly `self`), and the call is guarding the argument. `branch` is either `true` - * or `false`, indicating which branch of the guard is protecting the argument. + * (possibly `self`), and the call is guarding the argument. + * `acceptingValue` is either `true` or `false`, indicating which branch of + * the guard is protecting the parameter. */ extensible predicate barrierGuardModel( - string path, string input, string branch, string kind, string provenance, + string path, string input, string acceptingValue, string kind, string provenance, QlBuiltins::ExtensionId madId ); @@ -163,9 +164,9 @@ predicate interpretModelForTest(QlBuiltins::ExtensionId madId, string model) { model = "Barrier: " + path + "; " + output + "; " + kind ) or - exists(string path, string input, string branch, string kind | - barrierGuardModel(path, input, branch, kind, _, madId) and - model = "Barrier guard: " + path + "; " + input + "; " + branch + "; " + kind + exists(string path, string input, string acceptingValue, string kind | + barrierGuardModel(path, input, acceptingValue, kind, _, madId) and + model = "Barrier guard: " + path + "; " + input + "; " + acceptingValue + "; " + kind ) } @@ -275,10 +276,10 @@ private class FlowBarrierGuardFromModel extends FlowBarrierGuard::Range { } override predicate isBarrierGuard( - string input, string branch, string kind, Provenance provenance, string model + string input, string acceptingValue, string kind, Provenance provenance, string model ) { exists(QlBuiltins::ExtensionId madId | - barrierGuardModel(path, input, branch, kind, provenance, madId) and + barrierGuardModel(path, input, acceptingValue, kind, provenance, madId) and model = "MaD:" + madId.toString() ) } diff --git a/shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll b/shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll index 0c6e42d9066..ce980724778 100644 --- a/shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll +++ b/shared/dataflow/codeql/dataflow/internal/FlowSummaryImpl.qll @@ -388,11 +388,11 @@ module Make< /** * Holds if this element is a flow barrier guard of kind `kind`, for data - * flowing in as described by `input`, when `this` evaluates to `branch`. + * flowing in as described by `input`, when `this` evaluates to `acceptingValue`. */ pragma[nomagic] abstract predicate isBarrierGuard( - string input, string branch, string kind, Provenance provenance, string model + string input, string acceptingValue, string kind, Provenance provenance, string model ); } @@ -764,10 +764,10 @@ module Make< } private predicate isRelevantBarrierGuard( - BarrierGuardElement e, string input, string branch, string kind, Provenance provenance, - string model + BarrierGuardElement e, string input, string acceptingValue, string kind, + Provenance provenance, string model ) { - e.isBarrierGuard(input, branch, kind, provenance, model) and + e.isBarrierGuard(input, acceptingValue, kind, provenance, model) and ( provenance.isManual() or @@ -1588,11 +1588,11 @@ module Make< * Holds if `barrierGuard` is a relevant barrier guard element with input specification `inSpec`. */ predicate barrierGuardSpec( - BarrierGuardElement barrierGuard, SummaryComponentStack inSpec, string branch, string kind, - string model + BarrierGuardElement barrierGuard, SummaryComponentStack inSpec, string acceptingValue, + string kind, string model ) { exists(string input | - isRelevantBarrierGuard(barrierGuard, input, branch, kind, _, model) and + isRelevantBarrierGuard(barrierGuard, input, acceptingValue, kind, _, model) and External::interpretSpec(input, inSpec) ) } From 84c01bc255b549f9b34fece46a588c3e1205f6d6 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:26:39 +0100 Subject: [PATCH 20/49] C++: Upgrade query precision. --- cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql b/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql index 7f0a4833cb5..5842b9474f7 100644 --- a/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql +++ b/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql @@ -5,7 +5,7 @@ * @kind problem * @problem.severity error * @security-severity 7.5 - * @precision medium + * @precision high * @id cpp/wrong-type-format-argument * @tags reliability * correctness From fca567f6ea98abe390b446701fa82ff5c386b7d9 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:26:21 +0100 Subject: [PATCH 21/49] C++: Change note. --- .../src/change-notes/2026-04-02-wrong-type-format-argument.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 cpp/ql/src/change-notes/2026-04-02-wrong-type-format-argument.md diff --git a/cpp/ql/src/change-notes/2026-04-02-wrong-type-format-argument.md b/cpp/ql/src/change-notes/2026-04-02-wrong-type-format-argument.md new file mode 100644 index 00000000000..f8b9085dacc --- /dev/null +++ b/cpp/ql/src/change-notes/2026-04-02-wrong-type-format-argument.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The "Wrong type of arguments to formatting function" (`cpp/wrong-type-format-argument`) query has been upgraded to `high` precision. This query will now run in the default code scanning suite. From b41a4ff5e4c06ac0e2308786bf82490e00b232a3 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:28:19 +0100 Subject: [PATCH 22/49] C++: Upgrade query precision. --- cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql b/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql index 6747d177c80..b05bd637dc2 100644 --- a/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql +++ b/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql @@ -5,7 +5,7 @@ * @kind problem * @problem.severity warning * @security-severity 8.1 - * @precision medium + * @precision high * @id cpp/integer-multiplication-cast-to-long * @tags reliability * security From 909b55a40a2e4a85ce4ea04ff8d6a179c0b6399e Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:28:34 +0100 Subject: [PATCH 23/49] C++: Change note. --- .../2026-04-02-integer-multiplication-cast-to-long.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 cpp/ql/src/change-notes/2026-04-02-integer-multiplication-cast-to-long.md diff --git a/cpp/ql/src/change-notes/2026-04-02-integer-multiplication-cast-to-long.md b/cpp/ql/src/change-notes/2026-04-02-integer-multiplication-cast-to-long.md new file mode 100644 index 00000000000..cd6796b408f --- /dev/null +++ b/cpp/ql/src/change-notes/2026-04-02-integer-multiplication-cast-to-long.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The "Multiplication result converted to larger type" (`cpp/integer-multiplication-cast-to-long`) query has been upgraded to `high` precision. This query will now run in the default code scanning suite. From 520e95d92c255dc4e54ccaadc8c31121f0651ded Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:30:34 +0100 Subject: [PATCH 24/49] C++: Upgrade query precision. --- cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql index 3f330807304..7d9ef88adea 100644 --- a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql +++ b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql @@ -6,7 +6,7 @@ * @kind problem * @problem.severity warning * @security-severity 7.8 - * @precision medium + * @precision high * @tags reliability * security * external/cwe/cwe-190 From 9dbbdef4cbab1c6bb4f63825112f329b3f5f5fad Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:30:52 +0100 Subject: [PATCH 25/49] C++: Change note. --- .../src/change-notes/2026-04-02-comparison-with-wider-type.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 cpp/ql/src/change-notes/2026-04-02-comparison-with-wider-type.md diff --git a/cpp/ql/src/change-notes/2026-04-02-comparison-with-wider-type.md b/cpp/ql/src/change-notes/2026-04-02-comparison-with-wider-type.md new file mode 100644 index 00000000000..c84e1dba404 --- /dev/null +++ b/cpp/ql/src/change-notes/2026-04-02-comparison-with-wider-type.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The "Comparison of narrow type with wide type in loop condition" (`cpp/comparison-with-wider-type`) query has been upgraded to `high` precision. This query will now run in the default code scanning suite. From 2d02056e5c2434b85a853b8d414a5bffa327ed1c Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:34:54 +0100 Subject: [PATCH 26/49] C++: Second change note. --- .../change-notes/2026-04-02-implicit-function-declaration.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 cpp/ql/src/change-notes/2026-04-02-implicit-function-declaration.md diff --git a/cpp/ql/src/change-notes/2026-04-02-implicit-function-declaration.md b/cpp/ql/src/change-notes/2026-04-02-implicit-function-declaration.md new file mode 100644 index 00000000000..dd0dbd4bc7d --- /dev/null +++ b/cpp/ql/src/change-notes/2026-04-02-implicit-function-declaration.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The "Implicit function declaration" (`cpp/implicit-function-declaration`) query has been upgraded to `high` precision. From e83658ed06c43ea71f6d35adf1285592ae15148d Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:38:09 +0100 Subject: [PATCH 27/49] C++: Upgrade query precision. --- .../Underspecified Functions/ImplicitFunctionDeclaration.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql b/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql index 007ef71a163..0cf6c8b3714 100644 --- a/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql +++ b/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql @@ -5,7 +5,7 @@ * may lead to unpredictable behavior. * @kind problem * @problem.severity warning - * @precision medium + * @precision high * @id cpp/implicit-function-declaration * @tags correctness * maintainability From 9eabfc5fdc5d3b809641e4f8b81c1188b6d9a743 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:39:45 +0100 Subject: [PATCH 28/49] Update cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql Co-authored-by: Jeroen Ketema <93738568+jketema@users.noreply.github.com> --- .../Underspecified Functions/ImplicitFunctionDeclaration.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql b/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql index 0cf6c8b3714..00b29efbd0f 100644 --- a/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql +++ b/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql @@ -18,7 +18,7 @@ import TooManyArguments import semmle.code.cpp.commons.Exclusions /* - * This query is not compatible with build mode: none databases, and has + * This query is not compatible with build mode: none databases, and produces * no results on those databases. */ From 56af9a84ab4f6232bf03c10bc08def2a4611cb83 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:40:51 +0100 Subject: [PATCH 29/49] Update cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.qhelp --- .../Underspecified Functions/ImplicitFunctionDeclaration.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.qhelp b/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.qhelp index d9b5a022077..90a98e1bf57 100644 --- a/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.qhelp +++ b/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.qhelp @@ -14,7 +14,7 @@ function may behave unpredictably.

    This may indicate a misspelled function name, or that the required header containing the function declaration has not been included.

    -

    Note: This query is not compatible with build mode: none databases, and produces +

    Note: This query is not compatible with build mode: none databases, and produces no results on those databases.

    From 5866bcc8816a23b612043ba4dbb847eb4f36a019 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Thu, 2 Apr 2026 15:41:41 +0200 Subject: [PATCH 30/49] Actions: Add FP test for `actions/missing-workflow-permissions` --- .../Security/CWE-275/.github/workflows/perms11.yml | 9 +++++++++ .../Security/CWE-275/.github/workflows/perms12.yml | 11 +++++++++++ .../CWE-275/MissingActionsPermissions.expected | 1 + 3 files changed, 21 insertions(+) create mode 100644 actions/ql/test/query-tests/Security/CWE-275/.github/workflows/perms11.yml create mode 100644 actions/ql/test/query-tests/Security/CWE-275/.github/workflows/perms12.yml diff --git a/actions/ql/test/query-tests/Security/CWE-275/.github/workflows/perms11.yml b/actions/ql/test/query-tests/Security/CWE-275/.github/workflows/perms11.yml new file mode 100644 index 00000000000..717cdabc302 --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-275/.github/workflows/perms11.yml @@ -0,0 +1,9 @@ +on: + workflow_call: + +jobs: + build: + name: Build and test + runs-on: ubuntu-latest + steps: + - uses: actions/deploy-pages diff --git a/actions/ql/test/query-tests/Security/CWE-275/.github/workflows/perms12.yml b/actions/ql/test/query-tests/Security/CWE-275/.github/workflows/perms12.yml new file mode 100644 index 00000000000..25ac1f53248 --- /dev/null +++ b/actions/ql/test/query-tests/Security/CWE-275/.github/workflows/perms12.yml @@ -0,0 +1,11 @@ +on: + workflow_dispatch: + +permissions: + contents: read + id-token: write + pages: write + +jobs: + call-workflow: + uses: ./.github/workflows/perms11.yml diff --git a/actions/ql/test/query-tests/Security/CWE-275/MissingActionsPermissions.expected b/actions/ql/test/query-tests/Security/CWE-275/MissingActionsPermissions.expected index 52a045e0de2..74edf8a7d38 100644 --- a/actions/ql/test/query-tests/Security/CWE-275/MissingActionsPermissions.expected +++ b/actions/ql/test/query-tests/Security/CWE-275/MissingActionsPermissions.expected @@ -6,3 +6,4 @@ | .github/workflows/perms8.yml:7:5:10:33 | Job: build | Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {id-token: write, pages: write} | | .github/workflows/perms9.yml:7:5:10:44 | Job: build | Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {packages: write} | | .github/workflows/perms10.yml:7:5:10:33 | Job: build | Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read, models: read} | +| .github/workflows/perms11.yml:6:5:9:33 | Job: build | Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {id-token: write, pages: write} | From 74e6d3474d4a0f03a870a0ab5f23cb66f325ad3d Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Thu, 2 Apr 2026 15:42:45 +0200 Subject: [PATCH 31/49] Actions: Correctly check permissions in `actions/missing-workflow-permissions` --- .../CWE-275/MissingActionsPermissions.ql | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/actions/ql/src/Security/CWE-275/MissingActionsPermissions.ql b/actions/ql/src/Security/CWE-275/MissingActionsPermissions.ql index a8bd8a5f93d..00f601fd5da 100644 --- a/actions/ql/src/Security/CWE-275/MissingActionsPermissions.ql +++ b/actions/ql/src/Security/CWE-275/MissingActionsPermissions.ql @@ -26,10 +26,23 @@ string permissionsForJob(Job job) { "{" + concat(string permission | permission = jobNeedsPermission(job) | permission, ", ") + "}" } +predicate jobHasPermissions(Job job) { + exists(job.getPermissions()) + or + exists(job.getEnclosingWorkflow().getPermissions()) + or + // The workflow is reusable and cannot be triggered in any other way; check callers + exists(ReusableWorkflow r | r = job.getEnclosingWorkflow() | + not exists(Event e | e = r.getOn().getAnEvent() | e.getName() != "workflow_call") and + forall(Job caller | caller = job.getEnclosingWorkflow().(ReusableWorkflow).getACaller() | + jobHasPermissions(caller) + ) + ) +} + from Job job, string permissions where - not exists(job.getPermissions()) and - not exists(job.getEnclosingWorkflow().getPermissions()) and + not jobHasPermissions(job) and // exists a trigger event that is not a workflow_call exists(Event e | e = job.getATriggerEvent() and From 47409d1c599c2367c67e649bf89a4817cee9e1b2 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Thu, 2 Apr 2026 15:43:49 +0200 Subject: [PATCH 32/49] Actions: Update expected test results --- .../Security/CWE-275/MissingActionsPermissions.expected | 1 - 1 file changed, 1 deletion(-) diff --git a/actions/ql/test/query-tests/Security/CWE-275/MissingActionsPermissions.expected b/actions/ql/test/query-tests/Security/CWE-275/MissingActionsPermissions.expected index 74edf8a7d38..52a045e0de2 100644 --- a/actions/ql/test/query-tests/Security/CWE-275/MissingActionsPermissions.expected +++ b/actions/ql/test/query-tests/Security/CWE-275/MissingActionsPermissions.expected @@ -6,4 +6,3 @@ | .github/workflows/perms8.yml:7:5:10:33 | Job: build | Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {id-token: write, pages: write} | | .github/workflows/perms9.yml:7:5:10:44 | Job: build | Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {packages: write} | | .github/workflows/perms10.yml:7:5:10:33 | Job: build | Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read, models: read} | -| .github/workflows/perms11.yml:6:5:9:33 | Job: build | Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {id-token: write, pages: write} | From 87f9b9581ec3bb327ea78fbc82a6b3f3c4c2c355 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Thu, 2 Apr 2026 15:48:45 +0200 Subject: [PATCH 33/49] Actions: Add change note --- actions/ql/src/change-notes/2026-04-02-permissions.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 actions/ql/src/change-notes/2026-04-02-permissions.md diff --git a/actions/ql/src/change-notes/2026-04-02-permissions.md b/actions/ql/src/change-notes/2026-04-02-permissions.md new file mode 100644 index 00000000000..2672a30ef87 --- /dev/null +++ b/actions/ql/src/change-notes/2026-04-02-permissions.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The query `actions/missing-workflow-permissions` no longer produces false positive results on reusable workflows where all callers set permissions. \ No newline at end of file From 3769a8a48287688b1c7469233d32d46dcd636660 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 7 Apr 2026 12:51:56 +0100 Subject: [PATCH 34/49] C++: Update code scanning suite .expected. --- .../integration-tests/query-suite/cpp-code-scanning.qls.expected | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/ql/integration-tests/query-suite/cpp-code-scanning.qls.expected b/cpp/ql/integration-tests/query-suite/cpp-code-scanning.qls.expected index 57d240fd795..6875fbf43de 100644 --- a/cpp/ql/integration-tests/query-suite/cpp-code-scanning.qls.expected +++ b/cpp/ql/integration-tests/query-suite/cpp-code-scanning.qls.expected @@ -11,6 +11,7 @@ ql/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.ql ql/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql ql/cpp/ql/src/Likely Bugs/Format/SnprintfOverflow.ql ql/cpp/ql/src/Likely Bugs/Format/WrongNumberOfFormatArguments.ql +ql/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql ql/cpp/ql/src/Likely Bugs/Memory Management/AllocaInLoop.ql ql/cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow.ql ql/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql From f2292643a35362236f1e729bf7833839fb36bb56 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 7 Apr 2026 12:53:53 +0100 Subject: [PATCH 35/49] C++: Update code scanning suite .expected. --- .../integration-tests/query-suite/cpp-code-scanning.qls.expected | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/ql/integration-tests/query-suite/cpp-code-scanning.qls.expected b/cpp/ql/integration-tests/query-suite/cpp-code-scanning.qls.expected index 57d240fd795..6cc662aee3b 100644 --- a/cpp/ql/integration-tests/query-suite/cpp-code-scanning.qls.expected +++ b/cpp/ql/integration-tests/query-suite/cpp-code-scanning.qls.expected @@ -7,6 +7,7 @@ ql/cpp/ql/src/Diagnostics/ExtractedFiles.ql ql/cpp/ql/src/Diagnostics/ExtractionWarnings.ql ql/cpp/ql/src/Diagnostics/FailedExtractorInvocations.ql ql/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.ql +ql/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql ql/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.ql ql/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql ql/cpp/ql/src/Likely Bugs/Format/SnprintfOverflow.ql From 201af3fffcb9a79fbc49bc37ef05275abd0ce7f5 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Tue, 7 Apr 2026 12:59:31 +0100 Subject: [PATCH 36/49] C++: Update code scanning suite .expected. --- .../integration-tests/query-suite/cpp-code-scanning.qls.expected | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/ql/integration-tests/query-suite/cpp-code-scanning.qls.expected b/cpp/ql/integration-tests/query-suite/cpp-code-scanning.qls.expected index 57d240fd795..926efb34d85 100644 --- a/cpp/ql/integration-tests/query-suite/cpp-code-scanning.qls.expected +++ b/cpp/ql/integration-tests/query-suite/cpp-code-scanning.qls.expected @@ -28,6 +28,7 @@ ql/cpp/ql/src/Security/CWE/CWE-120/VeryLikelyOverrunWrite.ql ql/cpp/ql/src/Security/CWE/CWE-131/NoSpaceForZeroTerminator.ql ql/cpp/ql/src/Security/CWE/CWE-134/UncontrolledFormatString.ql ql/cpp/ql/src/Security/CWE/CWE-190/ArithmeticUncontrolled.ql +ql/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql ql/cpp/ql/src/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql ql/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql ql/cpp/ql/src/Security/CWE/CWE-311/CleartextFileWrite.ql From 8d79248ea767a3e931d0e1695d48c8b4d0b719ae Mon Sep 17 00:00:00 2001 From: Taus Date: Fri, 20 Mar 2026 13:55:49 +0000 Subject: [PATCH 37/49] Python: Port ModificationOfLocals.ql --- python/ql/src/Statements/ModificationOfLocals.ql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/ql/src/Statements/ModificationOfLocals.ql b/python/ql/src/Statements/ModificationOfLocals.ql index e4791a410f7..82529cbd6d0 100644 --- a/python/ql/src/Statements/ModificationOfLocals.ql +++ b/python/ql/src/Statements/ModificationOfLocals.ql @@ -12,10 +12,10 @@ */ import python -private import LegacyPointsTo +private import semmle.python.ApiGraphs -predicate originIsLocals(ControlFlowNodeWithPointsTo n) { - n.pointsTo(_, _, Value::named("locals").getACall()) +predicate originIsLocals(ControlFlowNode n) { + API::builtin("locals").getReturn().getAValueReachableFromSource().asCfgNode() = n } predicate modification_of_locals(ControlFlowNode f) { @@ -37,5 +37,5 @@ where // in module level scope `locals() == globals()` // see https://docs.python.org/3/library/functions.html#locals // FP report in https://github.com/github/codeql/issues/6674 - not a.getScope() instanceof ModuleScope + not a.getScope() instanceof Module select a, "Modification of the locals() dictionary will have no effect on the local variables." From e3688444d74582c29c0356f96ac80c19201f3166 Mon Sep 17 00:00:00 2001 From: Taus Date: Tue, 7 Apr 2026 21:39:30 +0000 Subject: [PATCH 38/49] Python: Also exclude class scope Changing the `locals()` dictionary actually _does_ change the attributes of the class being defined, so we shouldn't alert in this case. --- python/ql/src/Statements/ModificationOfLocals.ql | 5 ++++- python/ql/test/query-tests/Statements/general/test.py | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/python/ql/src/Statements/ModificationOfLocals.ql b/python/ql/src/Statements/ModificationOfLocals.ql index 82529cbd6d0..f32ddcf7884 100644 --- a/python/ql/src/Statements/ModificationOfLocals.ql +++ b/python/ql/src/Statements/ModificationOfLocals.ql @@ -37,5 +37,8 @@ where // in module level scope `locals() == globals()` // see https://docs.python.org/3/library/functions.html#locals // FP report in https://github.com/github/codeql/issues/6674 - not a.getScope() instanceof Module + not a.getScope() instanceof Module and + // in class level scope `locals()` reflects the class namespace, + // so modifications do take effect. + not a.getScope() instanceof Class select a, "Modification of the locals() dictionary will have no effect on the local variables." diff --git a/python/ql/test/query-tests/Statements/general/test.py b/python/ql/test/query-tests/Statements/general/test.py index eee63fa89e8..a5848f7c718 100644 --- a/python/ql/test/query-tests/Statements/general/test.py +++ b/python/ql/test/query-tests/Statements/general/test.py @@ -174,3 +174,9 @@ def assert_ok(seq): # False positive. ODASA-8042. Fixed in PR #2401. class false_positive: e = (x for x in []) + +# In class-level scope `locals()` reflects the class namespace, +# so modifications do take effect. +class MyClass: + locals()['x'] = 43 # OK + y = x From 95681bfad48b508fafec3e3f96ae52287e567dd2 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Mon, 9 Mar 2026 15:06:20 +0000 Subject: [PATCH 39/49] Rust: Fix performance issue with File.fromSource. --- rust/ql/lib/codeql/files/FileSystem.qll | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rust/ql/lib/codeql/files/FileSystem.qll b/rust/ql/lib/codeql/files/FileSystem.qll index cfab33b9a44..367f088504b 100644 --- a/rust/ql/lib/codeql/files/FileSystem.qll +++ b/rust/ql/lib/codeql/files/FileSystem.qll @@ -45,13 +45,16 @@ extensible predicate additionalExternalFile(string relativePath); /** A file. */ class File extends Container, Impl::File { + pragma[nomagic] + private string getRelativePath0() { result = this.getRelativePath() } + /** * Holds if this file was extracted from the source code of the target project * (rather than another location such as inside a dependency). */ predicate fromSource() { exists(ExtractorStep s | s.getAction() = "Extract" and s.getFile() = this) and - not additionalExternalFile(this.getRelativePath()) + not additionalExternalFile(this.getRelativePath0()) } /** From e72c1166640ddddfae86897cf24443da0dc90dd7 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Thu, 9 Apr 2026 11:18:25 +0100 Subject: [PATCH 40/49] Rust: Proposed improved solution. --- rust/ql/lib/codeql/files/FileSystem.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/ql/lib/codeql/files/FileSystem.qll b/rust/ql/lib/codeql/files/FileSystem.qll index 367f088504b..93ff7be604e 100644 --- a/rust/ql/lib/codeql/files/FileSystem.qll +++ b/rust/ql/lib/codeql/files/FileSystem.qll @@ -46,7 +46,7 @@ extensible predicate additionalExternalFile(string relativePath); /** A file. */ class File extends Container, Impl::File { pragma[nomagic] - private string getRelativePath0() { result = this.getRelativePath() } + private predicate isAdditionalExternalFile() { additionalExternalFile(this.getRelativePath()) } /** * Holds if this file was extracted from the source code of the target project @@ -54,7 +54,7 @@ class File extends Container, Impl::File { */ predicate fromSource() { exists(ExtractorStep s | s.getAction() = "Extract" and s.getFile() = this) and - not additionalExternalFile(this.getRelativePath0()) + not this.isAdditionalExternalFile() } /** From d622dabf3e12ee9b6fb2b6167627e958943bf687 Mon Sep 17 00:00:00 2001 From: Taus Date: Thu, 9 Apr 2026 13:06:45 +0000 Subject: [PATCH 41/49] Python: Add `create-extractor-pack.sh` for Python This allows us to build and test the extractor (for actual QL extraction -- not just the extractor unit tests) entirely from within the `github/codeql` repo, just as we do with Ruby. All that's needed is a `--search-path` argument that points to the repo root. --- python/scripts/create-extractor-pack.sh | 67 +++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100755 python/scripts/create-extractor-pack.sh diff --git a/python/scripts/create-extractor-pack.sh b/python/scripts/create-extractor-pack.sh new file mode 100755 index 00000000000..ae9248f6ccf --- /dev/null +++ b/python/scripts/create-extractor-pack.sh @@ -0,0 +1,67 @@ +#!/bin/bash +# +# Build a local Python extractor pack from source. +# +# Usage with the CodeQL CLI (run from the repository root): +# +# codeql database create -l python -s --search-path . +# codeql test run --search-path . python/ql/test/ +# +set -eux + +if [[ "$OSTYPE" == "linux-gnu"* ]]; then + platform="linux64" +elif [[ "$OSTYPE" == "darwin"* ]]; then + platform="osx64" +else + echo "Unknown OS" + exit 1 +fi + +cd "$(dirname "$0")/.." + +# Build the tsg-python Rust binary +(cd extractor/tsg-python && cargo build --release) +tsg_bin="extractor/tsg-python/target/release/tsg-python" + +# Generate python3src.zip from the Python extractor source. +# make_zips.py creates the zip in the source directory and then copies it to the +# given output directory. We use a temporary directory to avoid a same-file copy +# error, then move the zip back. +tmpdir=$(mktemp -d) +trap 'rm -rf "$tmpdir"' EXIT +(cd extractor && python3 make_zips.py "$tmpdir") +cp "$tmpdir/python3src.zip" extractor/python3src.zip + +# Assemble the extractor pack +rm -rf extractor-pack +mkdir -p extractor-pack/tools/${platform} + +# Root-level metadata and schema files +cp codeql-extractor.yml extractor-pack/ +cp ql/lib/semmlecode.python.dbscheme extractor-pack/ +cp ql/lib/semmlecode.python.dbscheme.stats extractor-pack/ + +# Python extractor engine files (into tools/) +cp extractor/python_tracer.py extractor-pack/tools/ +cp extractor/index.py extractor-pack/tools/ +cp extractor/setup.py extractor-pack/tools/ +cp extractor/convert_setup.py extractor-pack/tools/ +cp extractor/get_venv_lib.py extractor-pack/tools/ +cp extractor/imp.py extractor-pack/tools/ +cp extractor/LICENSE-PSF.md extractor-pack/tools/ +cp extractor/python3src.zip extractor-pack/tools/ +cp -r extractor/data extractor-pack/tools/ + +# Shell tool scripts (autobuild, pre-finalize, lgtm-scripts) +cp tools/autobuild.sh extractor-pack/tools/ +cp tools/autobuild.cmd extractor-pack/tools/ +cp tools/pre-finalize.sh extractor-pack/tools/ +cp tools/pre-finalize.cmd extractor-pack/tools/ +cp -r tools/lgtm-scripts extractor-pack/tools/ + +# Downgrades +cp -r downgrades extractor-pack/ + +# Platform-specific Rust binary +cp "${tsg_bin}" extractor-pack/tools/${platform}/tsg-python From cf4ab1d106dac871ea71cbad28d869c9feb8a978 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Thu, 9 Apr 2026 15:57:19 +0200 Subject: [PATCH 42/49] C#: Replace old-style unbind with pragmas. --- .../ql/lib/semmle/code/csharp/Conversion.qll | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/csharp/ql/lib/semmle/code/csharp/Conversion.qll b/csharp/ql/lib/semmle/code/csharp/Conversion.qll index ec7ef9cac95..fd2c680e9c7 100644 --- a/csharp/ql/lib/semmle/code/csharp/Conversion.qll +++ b/csharp/ql/lib/semmle/code/csharp/Conversion.qll @@ -232,14 +232,9 @@ private module Identity { */ pragma[nomagic] private predicate convTypeArguments(Type fromTypeArgument, Type toTypeArgument, int i) { - exists(int j | - fromTypeArgument = getTypeArgumentRanked(_, _, i) and - toTypeArgument = getTypeArgumentRanked(_, _, j) and - i <= j and - j <= i - | - convIdentity(fromTypeArgument, toTypeArgument) - ) + fromTypeArgument = getTypeArgumentRanked(_, _, pragma[only_bind_into](i)) and + toTypeArgument = getTypeArgumentRanked(_, _, pragma[only_bind_into](i)) and + convIdentity(fromTypeArgument, toTypeArgument) } pragma[nomagic] @@ -929,19 +924,16 @@ private module Variance { private predicate convTypeArguments( TypeArgument fromTypeArgument, TypeArgument toTypeArgument, int i, TVariance v ) { - exists(int j | - fromTypeArgument = getTypeArgumentRanked(_, _, i, _) and - toTypeArgument = getTypeArgumentRanked(_, _, j, _) and - i <= j and - j <= i - | + fromTypeArgument = getTypeArgumentRanked(_, _, pragma[only_bind_into](i), _) and + toTypeArgument = getTypeArgumentRanked(_, _, pragma[only_bind_into](i), _) and + ( convIdentity(fromTypeArgument, toTypeArgument) and v = TNone() or - convRefTypeTypeArgumentOut(fromTypeArgument, toTypeArgument, j) and + convRefTypeTypeArgumentOut(fromTypeArgument, toTypeArgument, i) and v = TOut() or - convRefTypeTypeArgumentIn(toTypeArgument, fromTypeArgument, j) and + convRefTypeTypeArgumentIn(toTypeArgument, fromTypeArgument, i) and v = TIn() ) } From bee39c9d51baf22e0ad600077ce06aaaa98abece Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Mon, 13 Apr 2026 11:08:58 +0200 Subject: [PATCH 43/49] C++: Fix `isCompiledAsC` join order Before on Abseil Windows for `cpp/too-few-arguments:`: ``` Pipeline standard for TooFewArguments::isCompiledAsC/1#52fe29e8@994f9bgp was evaluated in 12 iterations totaling 2ms (delta sizes total: 50). 1198778 ~3% {1} r1 = JOIN `TooFewArguments::isCompiledAsC/1#52fe29e8#prev_delta` WITH `Element::Element.getFile/0#2b8c8740_10#join_rhs` ON FIRST 1 OUTPUT Rhs.1 83 ~26% {1} | JOIN WITH includes ON FIRST 1 OUTPUT Rhs.1 50 ~4% {1} | AND NOT `TooFewArguments::isCompiledAsC/1#52fe29e8#prev`(FIRST 1) return r1 ``` After: ``` Pipeline standard for #File::File.getAnIncludedFile/0#dispred#e8d44cd1Plus#bf@b8d290i6 was evaluated in 11 iterations totaling 0ms (delta sizes total: 43). 47 ~0% {2} r1 = SCAN `#File::File.getAnIncludedFile/0#dispred#e8d44cd1Plus#bf#prev_delta` OUTPUT In.1, In.0 78 ~28% {2} | JOIN WITH `File::File.getAnIncludedFile/0#dispred#e8d44cd1` ON FIRST 1 OUTPUT Lhs.1, Rhs.1 43 ~0% {2} | AND NOT `#File::File.getAnIncludedFile/0#dispred#e8d44cd1Plus#bf#prev`(FIRST 2) return r1 [2026-04-13 11:05:25] Evaluated non-recursive predicate TooFewArguments::isCompiledAsC/1#52fe29e8@4a3eb9jk in 0ms (size: 49). Evaluated relational algebra for predicate TooFewArguments::isCompiledAsC/1#52fe29e8@4a3eb9jk with tuple counts: 1 ~0% {3} r1 = CONSTANT(unique int, unique string, unique string)[1,"compiled as c","1"] 1 ~0% {1} | JOIN WITH #fileannotationsMerge_1230#join_rhs ON FIRST 3 OUTPUT Rhs.3 48 ~0% {1} r2 = JOIN r1 WITH `#File::File.getAnIncludedFile/0#dispred#e8d44cd1Plus#bf` ON FIRST 1 OUTPUT Rhs.1 49 ~0% {1} r3 = r1 UNION r2 return r3 ``` --- .../Underspecified Functions/MistypedFunctionArguments.qll | 4 +--- .../Likely Bugs/Underspecified Functions/TooFewArguments.qll | 4 +--- .../Likely Bugs/Underspecified Functions/TooManyArguments.qll | 4 +--- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/cpp/ql/src/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.qll b/cpp/ql/src/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.qll index 2dced5d8d84..dbb457db505 100644 --- a/cpp/ql/src/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.qll +++ b/cpp/ql/src/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.qll @@ -79,9 +79,7 @@ private predicate hasZeroParamDecl(Function f) { // True if this file (or header) was compiled as a C file private predicate isCompiledAsC(File f) { - f.compiledAsC() - or - exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f) + exists(File src | src.compiledAsC() | src.getAnIncludedFile*() = f) } predicate mistypedFunctionArguments(FunctionCall fc, Function f, Parameter p) { diff --git a/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.qll b/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.qll index 218a54b36c5..fd323513a49 100644 --- a/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.qll +++ b/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.qll @@ -28,9 +28,7 @@ private predicate hasZeroParamDecl(Function f) { /* Holds if this file (or header) was compiled as a C file. */ private predicate isCompiledAsC(File f) { - f.compiledAsC() - or - exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f) + exists(File src | src.compiledAsC() | src.getAnIncludedFile*() = f) } /** Holds if `fc` is a call to `f` with too few arguments. */ diff --git a/cpp/ql/src/Likely Bugs/Underspecified Functions/TooManyArguments.qll b/cpp/ql/src/Likely Bugs/Underspecified Functions/TooManyArguments.qll index 7fba78b5550..ab2a98ae3a5 100644 --- a/cpp/ql/src/Likely Bugs/Underspecified Functions/TooManyArguments.qll +++ b/cpp/ql/src/Likely Bugs/Underspecified Functions/TooManyArguments.qll @@ -19,9 +19,7 @@ private predicate hasZeroParamDecl(Function f) { // True if this file (or header) was compiled as a C file private predicate isCompiledAsC(File f) { - f.compiledAsC() - or - exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f) + exists(File src | src.compiledAsC() | src.getAnIncludedFile*() = f) } predicate tooManyArguments(FunctionCall fc, Function f) { From fef582c858b30ee7542a0d5d07a0ec0161278d09 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 13 Apr 2026 11:22:49 +0200 Subject: [PATCH 44/49] JS: Add test case for Fastify per-route rate limiting --- .../MissingRateLimiting.expected | 7 ++++++ .../Security/CWE-770/MissingRateLimit/tst.js | 22 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/javascript/ql/test/query-tests/Security/CWE-770/MissingRateLimit/MissingRateLimiting.expected b/javascript/ql/test/query-tests/Security/CWE-770/MissingRateLimit/MissingRateLimiting.expected index 3d7bc2954eb..95f55486600 100644 --- a/javascript/ql/test/query-tests/Security/CWE-770/MissingRateLimit/MissingRateLimiting.expected +++ b/javascript/ql/test/query-tests/Security/CWE-770/MissingRateLimit/MissingRateLimiting.expected @@ -1,3 +1,4 @@ +#select | MissingRateLimiting.js:4:19:8:1 | functio ... ath);\\n} | This route handler performs $@, but is not rate-limited. | MissingRateLimiting.js:7:5:7:22 | res.sendFile(path) | a file system access | | MissingRateLimiting.js:25:19:25:20 | f1 | This route handler performs $@, but is not rate-limited. | MissingRateLimiting.js:13:5:13:22 | res.sendFile(path) | a file system access | | MissingRateLimiting.js:25:27:25:28 | f3 | This route handler performs $@, but is not rate-limited. | MissingRateLimiting.js:22:5:22:22 | res.sendFile(path) | a file system access | @@ -9,3 +10,9 @@ | tst.js:64:25:64:63 | functio ... req); } | This route handler performs $@, but is not rate-limited. | tst.js:64:46:64:60 | verifyUser(req) | authorization | | tst.js:76:25:76:53 | catchAs ... ndler1) | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization | | tst.js:88:24:88:40 | expensiveHandler1 | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization | +| tst.js:103:4:103:20 | expensiveHandler1 | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization | +| tst.js:110:4:110:20 | expensiveHandler1 | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization | +| tst.js:112:28:112:44 | expensiveHandler1 | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization | +testFailures +| tst.js:103:4:103:20 | This route handler performs $@, but is not rate-limited. | Unexpected result: Alert | +| tst.js:110:4:110:20 | This route handler performs $@, but is not rate-limited. | Unexpected result: Alert | diff --git a/javascript/ql/test/query-tests/Security/CWE-770/MissingRateLimit/tst.js b/javascript/ql/test/query-tests/Security/CWE-770/MissingRateLimit/tst.js index 4f778afef68..5b4312bbbe0 100644 --- a/javascript/ql/test/query-tests/Security/CWE-770/MissingRateLimit/tst.js +++ b/javascript/ql/test/query-tests/Security/CWE-770/MissingRateLimit/tst.js @@ -88,3 +88,25 @@ const fastifyApp = require('fastify')(); fastifyApp.get('/foo', expensiveHandler1); // $ Alert fastifyApp.register(require('fastify-rate-limit')); fastifyApp.get('/bar', expensiveHandler1); + +// Fastify per-route rate limiting via config.rateLimit +const fastifyApp2 = require('fastify')(); +fastifyApp2.register(require('@fastify/rate-limit')); + +fastifyApp2.post('/login', { + config: { + rateLimit: { + max: 3, + timeWindow: '1 minute' + } + } +}, expensiveHandler1); // OK - has per-route rateLimit config + +fastifyApp2.post('/signup', { + rateLimit: { + max: 5, + timeWindow: '1 minute' + } +}, expensiveHandler1); // OK - has per-route rateLimit directly in options + +fastifyApp2.post('/other', expensiveHandler1); // $ Alert - no rate limiting From 7a48409e386191f5fb0d95652bf0c431333c8d45 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 13 Apr 2026 11:23:08 +0200 Subject: [PATCH 45/49] JS: Recognize Fastify per-route rate limiting --- .../security/dataflow/MissingRateLimiting.qll | 18 ++++++++++++++++++ .../MissingRateLimiting.expected | 6 ------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/MissingRateLimiting.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/MissingRateLimiting.qll index 5f4ad1b3d73..8dd9c483144 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/MissingRateLimiting.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/MissingRateLimiting.qll @@ -191,3 +191,21 @@ class RouteHandlerLimitedByRateLimiterFlexible extends RateLimitingMiddleware in private class FastifyRateLimiter extends RateLimitingMiddleware { FastifyRateLimiter() { this = DataFlow::moduleImport("fastify-rate-limit") } } + +/** + * An options object with a `rateLimit` config passed to a Fastify shorthand route method, + * such as `fastify.post('/path', { config: { rateLimit: { ... } } }, handler)`. + */ +private class FastifyPerRouteRateLimit extends RateLimitingMiddleware { + FastifyPerRouteRateLimit() { + exists(Fastify::RouteSetup setup | + not setup.getMethodName() = ["route", "addHook"] and + setup.getNumArgument() >= 3 and + this.flowsTo(setup.getArgument(1)) + | + exists(this.getAPropertySource("config").getAPropertySource("rateLimit")) + or + exists(this.getAPropertySource("rateLimit")) + ) + } +} diff --git a/javascript/ql/test/query-tests/Security/CWE-770/MissingRateLimit/MissingRateLimiting.expected b/javascript/ql/test/query-tests/Security/CWE-770/MissingRateLimit/MissingRateLimiting.expected index 95f55486600..8d197d6e37f 100644 --- a/javascript/ql/test/query-tests/Security/CWE-770/MissingRateLimit/MissingRateLimiting.expected +++ b/javascript/ql/test/query-tests/Security/CWE-770/MissingRateLimit/MissingRateLimiting.expected @@ -1,4 +1,3 @@ -#select | MissingRateLimiting.js:4:19:8:1 | functio ... ath);\\n} | This route handler performs $@, but is not rate-limited. | MissingRateLimiting.js:7:5:7:22 | res.sendFile(path) | a file system access | | MissingRateLimiting.js:25:19:25:20 | f1 | This route handler performs $@, but is not rate-limited. | MissingRateLimiting.js:13:5:13:22 | res.sendFile(path) | a file system access | | MissingRateLimiting.js:25:27:25:28 | f3 | This route handler performs $@, but is not rate-limited. | MissingRateLimiting.js:22:5:22:22 | res.sendFile(path) | a file system access | @@ -10,9 +9,4 @@ | tst.js:64:25:64:63 | functio ... req); } | This route handler performs $@, but is not rate-limited. | tst.js:64:46:64:60 | verifyUser(req) | authorization | | tst.js:76:25:76:53 | catchAs ... ndler1) | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization | | tst.js:88:24:88:40 | expensiveHandler1 | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization | -| tst.js:103:4:103:20 | expensiveHandler1 | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization | -| tst.js:110:4:110:20 | expensiveHandler1 | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization | | tst.js:112:28:112:44 | expensiveHandler1 | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization | -testFailures -| tst.js:103:4:103:20 | This route handler performs $@, but is not rate-limited. | Unexpected result: Alert | -| tst.js:110:4:110:20 | This route handler performs $@, but is not rate-limited. | Unexpected result: Alert | From fcfb8c9c6bc5e76f877b9d5748a68a1d2084889c Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 13 Apr 2026 12:22:30 +0200 Subject: [PATCH 46/49] Add change note --- .../change-notes/2026-04-13-fastify-per-route-rate-limit.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 javascript/ql/src/change-notes/2026-04-13-fastify-per-route-rate-limit.md diff --git a/javascript/ql/src/change-notes/2026-04-13-fastify-per-route-rate-limit.md b/javascript/ql/src/change-notes/2026-04-13-fastify-per-route-rate-limit.md new file mode 100644 index 00000000000..56d52388524 --- /dev/null +++ b/javascript/ql/src/change-notes/2026-04-13-fastify-per-route-rate-limit.md @@ -0,0 +1,5 @@ +--- +category: minorAnalysis +--- +* The query `js/missing-rate-limiting` now takes Fastify per-route + rate limiting into account. From d3e580fd0ed0b52c531f0bdaa4e17033dd604c1d Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Mon, 13 Apr 2026 14:52:38 +0200 Subject: [PATCH 47/49] C#: Introduce Expr.getIntValue. --- .../ql/examples/snippets/integer_literal.ql | 2 +- .../ql/lib/semmle/code/csharp/Conversion.qll | 4 +-- .../code/csharp/commons/ComparisonTest.qll | 2 +- .../semmle/code/csharp/commons/Constants.qll | 4 +-- .../semmle/code/csharp/controlflow/Guards.qll | 27 +++++++------------ .../internal/rangeanalysis/ConstantUtils.qll | 2 +- .../ql/lib/semmle/code/csharp/exprs/Expr.qll | 7 +++++ .../system/runtime/CompilerServices.qll | 4 +-- csharp/ql/src/Likely Bugs/BadCheckOdd.ql | 10 +++---- .../src/Likely Bugs/MishandlingJapaneseEra.ql | 6 ++--- .../Likely Bugs/PossibleLossOfPrecision.ql | 4 +-- .../Security Features/InsufficientKeySize.ql | 6 ++--- 12 files changed, 38 insertions(+), 40 deletions(-) diff --git a/csharp/ql/examples/snippets/integer_literal.ql b/csharp/ql/examples/snippets/integer_literal.ql index 4546c1d174b..36791fc8c37 100644 --- a/csharp/ql/examples/snippets/integer_literal.ql +++ b/csharp/ql/examples/snippets/integer_literal.ql @@ -9,5 +9,5 @@ import csharp from IntegerLiteral literal -where literal.getValue().toInt() = 0 +where literal.getIntValue() = 0 select literal diff --git a/csharp/ql/lib/semmle/code/csharp/Conversion.qll b/csharp/ql/lib/semmle/code/csharp/Conversion.qll index fd2c680e9c7..e151944dc38 100644 --- a/csharp/ql/lib/semmle/code/csharp/Conversion.qll +++ b/csharp/ql/lib/semmle/code/csharp/Conversion.qll @@ -713,7 +713,7 @@ private class SignedIntegralConstantExpr extends Expr { } private predicate convConstantIntExpr(SignedIntegralConstantExpr e, SimpleType toType) { - exists(int n | n = e.getValue().toInt() | + exists(int n | n = e.getIntValue() | toType = any(SByteType t | n in [t.minValue() .. t.maxValue()]) or toType = any(ByteType t | n in [t.minValue() .. t.maxValue()]) @@ -730,7 +730,7 @@ private predicate convConstantIntExpr(SignedIntegralConstantExpr e, SimpleType t private predicate convConstantLongExpr(SignedIntegralConstantExpr e) { e.getType() instanceof LongType and - e.getValue().toInt() >= 0 + e.getIntValue() >= 0 } /** 6.1.10: Implicit reference conversions involving type parameters. */ diff --git a/csharp/ql/lib/semmle/code/csharp/commons/ComparisonTest.qll b/csharp/ql/lib/semmle/code/csharp/commons/ComparisonTest.qll index b4641560892..776e2e97c37 100644 --- a/csharp/ql/lib/semmle/code/csharp/commons/ComparisonTest.qll +++ b/csharp/ql/lib/semmle/code/csharp/commons/ComparisonTest.qll @@ -161,7 +161,7 @@ private newtype TComparisonTest = compare.getComparisonKind().isCompare() and outerKind = outer.getComparisonKind() and outer.getAnArgument() = compare.getExpr() and - i = outer.getAnArgument().getValue().toInt() + i = outer.getAnArgument().getIntValue() | outerKind.isEquality() and ( diff --git a/csharp/ql/lib/semmle/code/csharp/commons/Constants.qll b/csharp/ql/lib/semmle/code/csharp/commons/Constants.qll index 5025202eb21..a5f1bc43abe 100644 --- a/csharp/ql/lib/semmle/code/csharp/commons/Constants.qll +++ b/csharp/ql/lib/semmle/code/csharp/commons/Constants.qll @@ -32,13 +32,13 @@ private module ConstantComparisonOperation { private int maxValue(Expr expr) { if convertedType(expr) instanceof IntegralType and exists(expr.getValue()) - then result = expr.getValue().toInt() + then result = expr.getIntValue() else result = convertedType(expr).maxValue() } private int minValue(Expr expr) { if convertedType(expr) instanceof IntegralType and exists(expr.getValue()) - then result = expr.getValue().toInt() + then result = expr.getIntValue() else result = convertedType(expr).minValue() } diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll index 6c1eb8c06eb..66b591cfcd2 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll @@ -60,25 +60,16 @@ private module GuardsInput implements override boolean asBooleanValue() { boolConst(this, result) } } - private predicate intConst(Expr e, int i) { - e.getValue().toInt() = i and - ( - e.getType() instanceof Enum - or - e.getType() instanceof IntegralType - ) - } - private class IntegerConstant extends ConstantExpr { - IntegerConstant() { intConst(this, _) } + IntegerConstant() { exists(this.getIntValue()) } - override int asIntegerValue() { intConst(this, result) } + override int asIntegerValue() { result = this.getIntValue() } } private class EnumConst extends ConstantExpr { EnumConst() { this.getType() instanceof Enum and this.hasValue() } - override int asIntegerValue() { result = this.getValue().toInt() } + override int asIntegerValue() { result = this.getIntValue() } } private class StringConstant extends ConstantExpr instanceof StringLiteral { @@ -517,35 +508,35 @@ class EnumerableCollectionExpr extends Expr { | // x.Length == 0 ct.getComparisonKind().isEquality() and - ct.getAnArgument().getValue().toInt() = 0 and + ct.getAnArgument().getIntValue() = 0 and branch = isEmpty or // x.Length == k, k > 0 ct.getComparisonKind().isEquality() and - ct.getAnArgument().getValue().toInt() > 0 and + ct.getAnArgument().getIntValue() > 0 and branch = true and isEmpty = false or // x.Length != 0 ct.getComparisonKind().isInequality() and - ct.getAnArgument().getValue().toInt() = 0 and + ct.getAnArgument().getIntValue() = 0 and branch = isEmpty.booleanNot() or // x.Length != k, k != 0 ct.getComparisonKind().isInequality() and - ct.getAnArgument().getValue().toInt() != 0 and + ct.getAnArgument().getIntValue() != 0 and branch = false and isEmpty = false or // x.Length > k, k >= 0 ct.getComparisonKind().isLessThan() and - ct.getFirstArgument().getValue().toInt() >= 0 and + ct.getFirstArgument().getIntValue() >= 0 and branch = true and isEmpty = false or // x.Length >= k, k > 0 ct.getComparisonKind().isLessThanEquals() and - ct.getFirstArgument().getValue().toInt() > 0 and + ct.getFirstArgument().getIntValue() > 0 and branch = true and isEmpty = false ) diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll index e3f5deb9898..bea31ed7f55 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll @@ -23,7 +23,7 @@ predicate systemArrayLengthAccess(PropertyAccess pa) { * - a read of the `Length` of an array with `val` lengths. */ private predicate constantIntegerExpr(ExprNode e, int val) { - e.getValue().toInt() = val + e.getExpr().getIntValue() = val or exists(ExprNode src | e = getAnExplicitDefinitionRead(src) and diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll index 36eda82531c..a26afb00490 100644 --- a/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll +++ b/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll @@ -57,6 +57,13 @@ class Expr extends ControlFlowElement, @expr { /** Gets the value of this expression, if any */ string getValue() { expr_value(this, result) } + /** Gets the integer value of this expression, if any. */ + cached + int getIntValue() { + result = this.getValue().toInt() and + (this.getType() instanceof IntegralType or this.getType() instanceof Enum) + } + /** Holds if this expression has a value. */ final predicate hasValue() { exists(this.getValue()) } diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/system/runtime/CompilerServices.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/system/runtime/CompilerServices.qll index 2c0ba292b9c..aca2cb2cb1c 100644 --- a/csharp/ql/lib/semmle/code/csharp/frameworks/system/runtime/CompilerServices.qll +++ b/csharp/ql/lib/semmle/code/csharp/frameworks/system/runtime/CompilerServices.qll @@ -81,7 +81,7 @@ class SystemRuntimeCompilerServicesInlineArrayAttribute extends Attribute { /** * Gets the length of the inline array. */ - int getLength() { result = this.getConstructorArgument(0).getValue().toInt() } + int getLength() { result = this.getConstructorArgument(0).getIntValue() } } /** An attribute of type `System.Runtime.CompilerServices.OverloadResolutionPriority`. */ @@ -94,5 +94,5 @@ class SystemRuntimeCompilerServicesOverloadResolutionPriorityAttribute extends A /** * Gets the priority number. */ - int getPriority() { result = this.getConstructorArgument(0).getValue().toInt() } + int getPriority() { result = this.getConstructorArgument(0).getIntValue() } } diff --git a/csharp/ql/src/Likely Bugs/BadCheckOdd.ql b/csharp/ql/src/Likely Bugs/BadCheckOdd.ql index 34ae4b632ae..72924f9103d 100644 --- a/csharp/ql/src/Likely Bugs/BadCheckOdd.ql +++ b/csharp/ql/src/Likely Bugs/BadCheckOdd.ql @@ -13,7 +13,7 @@ import csharp predicate isDefinitelyPositive(Expr e) { - e.getValue().toInt() >= 0 or + e.getIntValue() >= 0 or e.(PropertyAccess).getTarget().hasName("Length") or e.(MethodCall).getTarget().hasUndecoratedName("Count") } @@ -23,12 +23,12 @@ where t.getLeftOperand() = lhs and t.getRightOperand() = rhs and not isDefinitelyPositive(lhs.getLeftOperand().stripCasts()) and - lhs.getRightOperand().(IntegerLiteral).getValue() = "2" and + lhs.getRightOperand().(IntegerLiteral).getIntValue() = 2 and ( - t instanceof EQExpr and rhs.getValue() = "1" and parity = "oddness" + t instanceof EQExpr and rhs.getIntValue() = 1 and parity = "oddness" or - t instanceof NEExpr and rhs.getValue() = "1" and parity = "evenness" + t instanceof NEExpr and rhs.getIntValue() = 1 and parity = "evenness" or - t instanceof GTExpr and rhs.getValue() = "0" and parity = "oddness" + t instanceof GTExpr and rhs.getIntValue() = 0 and parity = "oddness" ) select t, "Possibly invalid test for " + parity + ". This will fail for negative numbers." diff --git a/csharp/ql/src/Likely Bugs/MishandlingJapaneseEra.ql b/csharp/ql/src/Likely Bugs/MishandlingJapaneseEra.ql index c8df36bf7bf..3e54a3a00db 100644 --- a/csharp/ql/src/Likely Bugs/MishandlingJapaneseEra.ql +++ b/csharp/ql/src/Likely Bugs/MishandlingJapaneseEra.ql @@ -27,8 +27,8 @@ predicate isExactEraStartDateCreation(ObjectCreation cr) { cr.getType().hasFullyQualifiedName("System", "DateTime") or cr.getType().hasFullyQualifiedName("System", "DateTimeOffset") ) and - isEraStart(cr.getArgument(0).getValue().toInt(), cr.getArgument(1).getValue().toInt(), - cr.getArgument(2).getValue().toInt()) + isEraStart(cr.getArgument(0).getIntValue(), cr.getArgument(1).getIntValue(), + cr.getArgument(2).getIntValue()) } predicate isDateFromJapaneseCalendarToDateTime(MethodCall mc) { @@ -44,7 +44,7 @@ predicate isDateFromJapaneseCalendarToDateTime(MethodCall mc) { mc.getNumberOfArguments() = 7 // implicitly current era or mc.getNumberOfArguments() = 8 and - mc.getArgument(7).getValue() = "0" + mc.getArgument(7).getIntValue() = 0 ) // explicitly current era } diff --git a/csharp/ql/src/Likely Bugs/PossibleLossOfPrecision.ql b/csharp/ql/src/Likely Bugs/PossibleLossOfPrecision.ql index 8b719bb92a5..1c41d24fb3c 100644 --- a/csharp/ql/src/Likely Bugs/PossibleLossOfPrecision.ql +++ b/csharp/ql/src/Likely Bugs/PossibleLossOfPrecision.ql @@ -40,8 +40,8 @@ predicate convertedToFloatOrDecimal(Expr e, Type t) { /** Holds if `div` is an exact integer division. */ predicate exactDivision(DivExpr div) { exists(int numerator, int denominator | - numerator = div.getNumerator().stripCasts().getValue().toInt() and - denominator = div.getDenominator().stripCasts().getValue().toInt() and + numerator = div.getNumerator().stripCasts().getIntValue() and + denominator = div.getDenominator().stripCasts().getIntValue() and numerator % denominator = 0 ) } diff --git a/csharp/ql/src/Security Features/InsufficientKeySize.ql b/csharp/ql/src/Security Features/InsufficientKeySize.ql index 02be9125835..98a7852fbaf 100644 --- a/csharp/ql/src/Security Features/InsufficientKeySize.ql +++ b/csharp/ql/src/Security Features/InsufficientKeySize.ql @@ -20,7 +20,7 @@ predicate incorrectUseOfRC2(Assignment e, string msg) { .getDeclaringType() .hasFullyQualifiedName("System.Security.Cryptography", "RC2CryptoServiceProvider") ) and - e.getRightOperand().getValue().toInt() < 128 and + e.getRightOperand().getIntValue() < 128 and msg = "Key size should be at least 128 bits for RC2 encryption." } @@ -28,7 +28,7 @@ predicate incorrectUseOfDsa(ObjectCreation e, string msg) { e.getTarget() .getDeclaringType() .hasFullyQualifiedName("System.Security.Cryptography", "DSACryptoServiceProvider") and - exists(Expr i | e.getArgument(0) = i and i.getValue().toInt() < 2048) and + exists(Expr i | e.getArgument(0) = i and i.getIntValue() < 2048) and msg = "Key size should be at least 2048 bits for DSA encryption." } @@ -36,7 +36,7 @@ predicate incorrectUseOfRsa(ObjectCreation e, string msg) { e.getTarget() .getDeclaringType() .hasFullyQualifiedName("System.Security.Cryptography", "RSACryptoServiceProvider") and - exists(Expr i | e.getArgument(0) = i and i.getValue().toInt() < 2048) and + exists(Expr i | e.getArgument(0) = i and i.getIntValue() < 2048) and msg = "Key size should be at least 2048 bits for RSA encryption." } From 19c4b2ff8f2c47fcfda886ed5fcf427f43408123 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Mon, 13 Apr 2026 15:44:41 +0200 Subject: [PATCH 48/49] C++: Use `getConvSpecString` instead of `getConvSpecOffset` and `substring` --- cpp/ql/lib/semmle/code/cpp/commons/Printf.qll | 19 +++++++++++-------- cpp/ql/lib/semmle/code/cpp/commons/Scanf.qll | 13 +++++++++---- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll b/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll index d189dd36f87..253838d2ec3 100644 --- a/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll +++ b/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll @@ -459,6 +459,13 @@ class FormatLiteral extends Literal instanceof StringLiteral { */ int getConvSpecOffset(int n) { result = this.getFormat().indexOf("%", n, 0) } + /** + * Gets the nth conversion specifier string. + */ + private string getConvSpecString(int n) { + n >= 0 and result = "%" + this.getFormat().splitAt("%", n + 1) + } + /* * Each of these predicates gets a regular expressions to match each individual * parts of a conversion specifier. @@ -524,10 +531,8 @@ class FormatLiteral extends Literal instanceof StringLiteral { int n, string spec, string params, string flags, string width, string prec, string len, string conv ) { - exists(int offset, string fmt, string rst, string regexp | - offset = this.getConvSpecOffset(n) and - fmt = this.getFormat() and - rst = fmt.substring(offset, fmt.length()) and + exists(string rst, string regexp | + rst = this.getConvSpecString(n) and regexp = this.getConvSpecRegexp() and ( spec = rst.regexpCapture(regexp, 1) and @@ -554,10 +559,8 @@ class FormatLiteral extends Literal instanceof StringLiteral { * Gets the nth conversion specifier (including the initial `%`). */ string getConvSpec(int n) { - exists(int offset, string fmt, string rst, string regexp | - offset = this.getConvSpecOffset(n) and - fmt = this.getFormat() and - rst = fmt.substring(offset, fmt.length()) and + exists(string rst, string regexp | + rst = this.getConvSpecString(n) and regexp = this.getConvSpecRegexp() and result = rst.regexpCapture(regexp, 1) ) diff --git a/cpp/ql/lib/semmle/code/cpp/commons/Scanf.qll b/cpp/ql/lib/semmle/code/cpp/commons/Scanf.qll index f032ba4749e..b683530e822 100644 --- a/cpp/ql/lib/semmle/code/cpp/commons/Scanf.qll +++ b/cpp/ql/lib/semmle/code/cpp/commons/Scanf.qll @@ -194,6 +194,13 @@ class ScanfFormatLiteral extends Expr { ) } + /** + * Gets the nth conversion specifier string. + */ + private string getConvSpecString(int n) { + n >= 0 and result = "%" + this.getFormat().splitAt("%", n + 1) + } + /** * Gets the regular expression to match each individual part of a conversion specifier. */ @@ -227,10 +234,8 @@ class ScanfFormatLiteral extends Expr { * specifier. */ predicate parseConvSpec(int n, string spec, string width, string len, string conv) { - exists(int offset, string fmt, string rst, string regexp | - offset = this.getConvSpecOffset(n) and - fmt = this.getFormat() and - rst = fmt.substring(offset, fmt.length()) and + exists(string rst, string regexp | + rst = this.getConvSpecString(n) and regexp = this.getConvSpecRegexp() and ( spec = rst.regexpCapture(regexp, 1) and From 26715fc95c40363ffdef45fafb4f9a2745ddac69 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 14 Apr 2026 08:03:51 +0200 Subject: [PATCH 49/49] C++: Rename `rst` to `convSpec` --- cpp/ql/lib/semmle/code/cpp/commons/Printf.qll | 28 +++++++++---------- cpp/ql/lib/semmle/code/cpp/commons/Scanf.qll | 12 ++++---- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll b/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll index 253838d2ec3..624465761c2 100644 --- a/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll +++ b/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll @@ -531,20 +531,20 @@ class FormatLiteral extends Literal instanceof StringLiteral { int n, string spec, string params, string flags, string width, string prec, string len, string conv ) { - exists(string rst, string regexp | - rst = this.getConvSpecString(n) and + exists(string convSpec, string regexp | + convSpec = this.getConvSpecString(n) and regexp = this.getConvSpecRegexp() and ( - spec = rst.regexpCapture(regexp, 1) and - params = rst.regexpCapture(regexp, 2) and - flags = rst.regexpCapture(regexp, 3) and - width = rst.regexpCapture(regexp, 4) and - prec = rst.regexpCapture(regexp, 5) and - len = rst.regexpCapture(regexp, 6) and - conv = rst.regexpCapture(regexp, 7) + spec = convSpec.regexpCapture(regexp, 1) and + params = convSpec.regexpCapture(regexp, 2) and + flags = convSpec.regexpCapture(regexp, 3) and + width = convSpec.regexpCapture(regexp, 4) and + prec = convSpec.regexpCapture(regexp, 5) and + len = convSpec.regexpCapture(regexp, 6) and + conv = convSpec.regexpCapture(regexp, 7) or - spec = rst.regexpCapture(regexp, 1) and - not exists(rst.regexpCapture(regexp, 2)) and + spec = convSpec.regexpCapture(regexp, 1) and + not exists(convSpec.regexpCapture(regexp, 2)) and params = "" and flags = "" and width = "" and @@ -559,10 +559,10 @@ class FormatLiteral extends Literal instanceof StringLiteral { * Gets the nth conversion specifier (including the initial `%`). */ string getConvSpec(int n) { - exists(string rst, string regexp | - rst = this.getConvSpecString(n) and + exists(string convSpec, string regexp | + convSpec = this.getConvSpecString(n) and regexp = this.getConvSpecRegexp() and - result = rst.regexpCapture(regexp, 1) + result = convSpec.regexpCapture(regexp, 1) ) } diff --git a/cpp/ql/lib/semmle/code/cpp/commons/Scanf.qll b/cpp/ql/lib/semmle/code/cpp/commons/Scanf.qll index b683530e822..98280a522cf 100644 --- a/cpp/ql/lib/semmle/code/cpp/commons/Scanf.qll +++ b/cpp/ql/lib/semmle/code/cpp/commons/Scanf.qll @@ -234,14 +234,14 @@ class ScanfFormatLiteral extends Expr { * specifier. */ predicate parseConvSpec(int n, string spec, string width, string len, string conv) { - exists(string rst, string regexp | - rst = this.getConvSpecString(n) and + exists(string convSpec, string regexp | + convSpec = this.getConvSpecString(n) and regexp = this.getConvSpecRegexp() and ( - spec = rst.regexpCapture(regexp, 1) and - width = rst.regexpCapture(regexp, 2) and - len = rst.regexpCapture(regexp, 3) and - conv = rst.regexpCapture(regexp, 4) + spec = convSpec.regexpCapture(regexp, 1) and + width = convSpec.regexpCapture(regexp, 2) and + len = convSpec.regexpCapture(regexp, 3) and + conv = convSpec.regexpCapture(regexp, 4) ) ) }