diff --git a/MODULE.bazel b/MODULE.bazel
index d5c2c8784e26..16b4a4691f8a 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -27,7 +27,7 @@ bazel_dep(name = "abseil-cpp", version = "20260107.1", repo_name = "absl")
bazel_dep(name = "nlohmann_json", version = "3.11.3", repo_name = "json")
bazel_dep(name = "fmt", version = "12.1.0-codeql.1")
bazel_dep(name = "rules_kotlin", version = "2.2.2-codeql.1")
-bazel_dep(name = "gazelle", version = "0.47.0")
+bazel_dep(name = "gazelle", version = "0.50.0")
bazel_dep(name = "rules_dotnet", version = "0.21.5-codeql.1")
bazel_dep(name = "googletest", version = "1.17.0.bcr.2")
bazel_dep(name = "rules_rust", version = "0.69.0")
diff --git a/actions/ql/lib/CHANGELOG.md b/actions/ql/lib/CHANGELOG.md
index 03201c9603af..e84ba38d1801 100644
--- a/actions/ql/lib/CHANGELOG.md
+++ b/actions/ql/lib/CHANGELOG.md
@@ -1,3 +1,13 @@
+## 0.4.34
+
+### Minor Analysis Improvements
+
+* Removed false positive injection sink models for the `context` input of `docker/build-push-action` and the `allowed-endpoints` input of `step-security/harden-runner`.
+
+## 0.4.33
+
+No user-facing changes.
+
## 0.4.32
No user-facing changes.
diff --git a/actions/ql/lib/change-notes/released/0.4.33.md b/actions/ql/lib/change-notes/released/0.4.33.md
new file mode 100644
index 000000000000..99c04e352dff
--- /dev/null
+++ b/actions/ql/lib/change-notes/released/0.4.33.md
@@ -0,0 +1,3 @@
+## 0.4.33
+
+No user-facing changes.
diff --git a/actions/ql/lib/change-notes/released/0.4.34.md b/actions/ql/lib/change-notes/released/0.4.34.md
new file mode 100644
index 000000000000..23b06db49679
--- /dev/null
+++ b/actions/ql/lib/change-notes/released/0.4.34.md
@@ -0,0 +1,5 @@
+## 0.4.34
+
+### Minor Analysis Improvements
+
+* Removed false positive injection sink models for the `context` input of `docker/build-push-action` and the `allowed-endpoints` input of `step-security/harden-runner`.
diff --git a/actions/ql/lib/codeql-pack.release.yml b/actions/ql/lib/codeql-pack.release.yml
index 3201cd9b0637..69fb16e4c39f 100644
--- a/actions/ql/lib/codeql-pack.release.yml
+++ b/actions/ql/lib/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.4.32
+lastReleaseVersion: 0.4.34
diff --git a/actions/ql/lib/ext/manual/docker_build-push-action.model.yml b/actions/ql/lib/ext/manual/docker_build-push-action.model.yml
deleted file mode 100644
index 116c231c30a4..000000000000
--- a/actions/ql/lib/ext/manual/docker_build-push-action.model.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-extensions:
- - addsTo:
- pack: codeql/actions-all
- extensible: actionsSinkModel
- data:
- - ["docker/build-push-action", "*", "input.context", "code-injection", "manual"]
\ No newline at end of file
diff --git a/actions/ql/lib/ext/manual/step-security_harden-runner.model.yml b/actions/ql/lib/ext/manual/step-security_harden-runner.model.yml
deleted file mode 100644
index 129c8beb0202..000000000000
--- a/actions/ql/lib/ext/manual/step-security_harden-runner.model.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-extensions:
- - addsTo:
- pack: codeql/actions-all
- extensible: actionsSinkModel
- data:
- - ["step-security/harden-runner", "*", "input.allowed-endpoints", "command-injection", "manual"]
diff --git a/actions/ql/lib/qlpack.yml b/actions/ql/lib/qlpack.yml
index bc4a8ba134ba..6e78fc546b33 100644
--- a/actions/ql/lib/qlpack.yml
+++ b/actions/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/actions-all
-version: 0.4.33-dev
+version: 0.4.35-dev
library: true
warnOnImplicitThis: true
dependencies:
diff --git a/actions/ql/src/CHANGELOG.md b/actions/ql/src/CHANGELOG.md
index e42a19a8168f..96f8d2662060 100644
--- a/actions/ql/src/CHANGELOG.md
+++ b/actions/ql/src/CHANGELOG.md
@@ -1,3 +1,17 @@
+## 0.6.26
+
+### Major Analysis Improvements
+
+* Fixed alert messages in `actions/artifact-poisoning/critical` and `actions/artifact-poisoning/medium` as they previously included a redundant placeholder in the alert message that would on occasion contain a long block of yml that makes the alert difficult to understand. Also improved the wording to make it clearer that it is not the artifact that is being poisoned, but instead a potentially untrusted artifact that is consumed. Finally, changed the alert location to be the source, to align more with other queries reporting an artifact (e.g. zipslip) which is more useful.
+
+### Minor Analysis Improvements
+
+* The query `actions/missing-workflow-permissions` no longer produces false positive results on reusable workflows where all callers set permissions.
+
+## 0.6.25
+
+No user-facing changes.
+
## 0.6.24
No user-facing changes.
@@ -159,7 +173,7 @@ No user-facing changes.
* `actions/if-expression-always-true/critical`
* `actions/if-expression-always-true/high`
* `actions/unnecessary-use-of-advanced-config`
-
+
* The following query has been moved from the `code-scanning` suite to the `security-extended`
suite. Any existing alerts for this query will be closed automatically unless the analysis is
configured to use the `security-extended` suite.
diff --git a/actions/ql/src/Security/CWE-275/MissingActionsPermissions.ql b/actions/ql/src/Security/CWE-275/MissingActionsPermissions.ql
index a8bd8a5f93dc..00f601fd5daf 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
diff --git a/actions/ql/src/Security/CWE-829/ArtifactPoisoningCritical.ql b/actions/ql/src/Security/CWE-829/ArtifactPoisoningCritical.ql
index 24ecb4b03397..be49de830c33 100644
--- a/actions/ql/src/Security/CWE-829/ArtifactPoisoningCritical.ql
+++ b/actions/ql/src/Security/CWE-829/ArtifactPoisoningCritical.ql
@@ -20,6 +20,6 @@ from ArtifactPoisoningFlow::PathNode source, ArtifactPoisoningFlow::PathNode sin
where
ArtifactPoisoningFlow::flowPath(source, sink) and
event = getRelevantEventInPrivilegedContext(sink.getNode())
-select sink.getNode(), source, sink,
- "Potential artifact poisoning in $@, which may be controlled by an external user ($@).", sink,
- sink.getNode().toString(), event, event.getName()
+select source.getNode(), source, sink,
+ "Potential artifact poisoning; the artifact being consumed has contents that may be controlled by an external user ($@).",
+ event, event.getName()
diff --git a/actions/ql/src/Security/CWE-829/ArtifactPoisoningMedium.ql b/actions/ql/src/Security/CWE-829/ArtifactPoisoningMedium.ql
index d2aff7da95ff..49dc856e5665 100644
--- a/actions/ql/src/Security/CWE-829/ArtifactPoisoningMedium.ql
+++ b/actions/ql/src/Security/CWE-829/ArtifactPoisoningMedium.ql
@@ -20,6 +20,5 @@ from ArtifactPoisoningFlow::PathNode source, ArtifactPoisoningFlow::PathNode sin
where
ArtifactPoisoningFlow::flowPath(source, sink) and
inNonPrivilegedContext(sink.getNode().asExpr())
-select sink.getNode(), source, sink,
- "Potential artifact poisoning in $@, which may be controlled by an external user.", sink,
- sink.getNode().toString()
+select source.getNode(), source, sink,
+ "Potential artifact poisoning; the artifact being consumed has contents that may be controlled by an external user."
diff --git a/actions/ql/src/change-notes/released/0.6.25.md b/actions/ql/src/change-notes/released/0.6.25.md
new file mode 100644
index 000000000000..b9d9e69c728d
--- /dev/null
+++ b/actions/ql/src/change-notes/released/0.6.25.md
@@ -0,0 +1,3 @@
+## 0.6.25
+
+No user-facing changes.
diff --git a/actions/ql/src/change-notes/released/0.6.26.md b/actions/ql/src/change-notes/released/0.6.26.md
new file mode 100644
index 000000000000..8bf43e639079
--- /dev/null
+++ b/actions/ql/src/change-notes/released/0.6.26.md
@@ -0,0 +1,9 @@
+## 0.6.26
+
+### Major Analysis Improvements
+
+* Fixed alert messages in `actions/artifact-poisoning/critical` and `actions/artifact-poisoning/medium` as they previously included a redundant placeholder in the alert message that would on occasion contain a long block of yml that makes the alert difficult to understand. Also improved the wording to make it clearer that it is not the artifact that is being poisoned, but instead a potentially untrusted artifact that is consumed. Finally, changed the alert location to be the source, to align more with other queries reporting an artifact (e.g. zipslip) which is more useful.
+
+### Minor Analysis Improvements
+
+* The query `actions/missing-workflow-permissions` no longer produces false positive results on reusable workflows where all callers set permissions.
diff --git a/actions/ql/src/codeql-pack.release.yml b/actions/ql/src/codeql-pack.release.yml
index f4aa7271ace7..e83bac0046e3 100644
--- a/actions/ql/src/codeql-pack.release.yml
+++ b/actions/ql/src/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.6.24
+lastReleaseVersion: 0.6.26
diff --git a/actions/ql/src/qlpack.yml b/actions/ql/src/qlpack.yml
index 3f76a5273f15..c815afc498c8 100644
--- a/actions/ql/src/qlpack.yml
+++ b/actions/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/actions-queries
-version: 0.6.25-dev
+version: 0.6.27-dev
library: false
warnOnImplicitThis: true
groups: [actions, queries]
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 000000000000..717cdabc3025
--- /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 000000000000..25ac1f532481
--- /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-829/ArtifactPoisoningCritical.expected b/actions/ql/test/query-tests/Security/CWE-829/ArtifactPoisoningCritical.expected
index 2d29cd9b79b4..3c5f6bf93e98 100644
--- a/actions/ql/test/query-tests/Security/CWE-829/ArtifactPoisoningCritical.expected
+++ b/actions/ql/test/query-tests/Security/CWE-829/ArtifactPoisoningCritical.expected
@@ -55,21 +55,21 @@ nodes
| .github/workflows/test25.yml:39:14:40:45 | ./gradlew buildScanPublishPrevious\n | semmle.label | ./gradlew buildScanPublishPrevious\n |
subpaths
#select
-| .github/workflows/artifactpoisoning11.yml:38:11:38:77 | ./sonarcloud-data/x.py build -j$(nproc) --compiler gcc --skip-build | .github/workflows/artifactpoisoning11.yml:13:9:32:6 | Uses Step | .github/workflows/artifactpoisoning11.yml:38:11:38:77 | ./sonarcloud-data/x.py build -j$(nproc) --compiler gcc --skip-build | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/artifactpoisoning11.yml:38:11:38:77 | ./sonarcloud-data/x.py build -j$(nproc) --compiler gcc --skip-build | ./sonarcloud-data/x.py build -j$(nproc) --compiler gcc --skip-build | .github/workflows/artifactpoisoning11.yml:4:3:4:14 | workflow_run | workflow_run |
-| .github/workflows/artifactpoisoning12.yml:38:11:38:25 | python foo/x.py | .github/workflows/artifactpoisoning12.yml:13:9:32:6 | Uses Step | .github/workflows/artifactpoisoning12.yml:38:11:38:25 | python foo/x.py | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/artifactpoisoning12.yml:38:11:38:25 | python foo/x.py | python foo/x.py | .github/workflows/artifactpoisoning12.yml:4:3:4:14 | workflow_run | workflow_run |
-| .github/workflows/artifactpoisoning21.yml:19:14:20:21 | sh foo/cmd\n | .github/workflows/artifactpoisoning21.yml:13:9:18:6 | Uses Step | .github/workflows/artifactpoisoning21.yml:19:14:20:21 | sh foo/cmd\n | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/artifactpoisoning21.yml:19:14:20:21 | sh foo/cmd\n | sh foo/cmd\n | .github/workflows/artifactpoisoning21.yml:4:3:4:14 | workflow_run | workflow_run |
-| .github/workflows/artifactpoisoning22.yml:18:14:18:19 | sh cmd | .github/workflows/artifactpoisoning22.yml:13:9:17:6 | Uses Step | .github/workflows/artifactpoisoning22.yml:18:14:18:19 | sh cmd | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/artifactpoisoning22.yml:18:14:18:19 | sh cmd | sh cmd | .github/workflows/artifactpoisoning22.yml:4:3:4:14 | workflow_run | workflow_run |
-| .github/workflows/artifactpoisoning31.yml:19:14:19:22 | ./foo/cmd | .github/workflows/artifactpoisoning31.yml:13:9:15:6 | Run Step | .github/workflows/artifactpoisoning31.yml:19:14:19:22 | ./foo/cmd | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/artifactpoisoning31.yml:19:14:19:22 | ./foo/cmd | ./foo/cmd | .github/workflows/artifactpoisoning31.yml:4:3:4:14 | workflow_run | workflow_run |
-| .github/workflows/artifactpoisoning32.yml:17:14:18:20 | ./bar/cmd\n | .github/workflows/artifactpoisoning32.yml:13:9:16:6 | Run Step | .github/workflows/artifactpoisoning32.yml:17:14:18:20 | ./bar/cmd\n | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/artifactpoisoning32.yml:17:14:18:20 | ./bar/cmd\n | ./bar/cmd\n | .github/workflows/artifactpoisoning32.yml:4:3:4:14 | workflow_run | workflow_run |
-| .github/workflows/artifactpoisoning33.yml:17:14:18:20 | ./bar/cmd\n | .github/workflows/artifactpoisoning33.yml:13:9:16:6 | Run Step | .github/workflows/artifactpoisoning33.yml:17:14:18:20 | ./bar/cmd\n | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/artifactpoisoning33.yml:17:14:18:20 | ./bar/cmd\n | ./bar/cmd\n | .github/workflows/artifactpoisoning33.yml:4:3:4:14 | workflow_run | workflow_run |
-| .github/workflows/artifactpoisoning34.yml:20:14:22:23 | npm install\nnpm run lint\n | .github/workflows/artifactpoisoning34.yml:13:9:16:6 | Run Step | .github/workflows/artifactpoisoning34.yml:20:14:22:23 | npm install\nnpm run lint\n | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/artifactpoisoning34.yml:20:14:22:23 | npm install\nnpm run lint\n | npm install\nnpm run lint\n | .github/workflows/artifactpoisoning34.yml:4:3:4:14 | workflow_run | workflow_run |
-| .github/workflows/artifactpoisoning41.yml:22:14:22:22 | ./foo/cmd | .github/workflows/artifactpoisoning41.yml:13:9:21:6 | Run Step | .github/workflows/artifactpoisoning41.yml:22:14:22:22 | ./foo/cmd | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/artifactpoisoning41.yml:22:14:22:22 | ./foo/cmd | ./foo/cmd | .github/workflows/artifactpoisoning41.yml:4:3:4:14 | workflow_run | workflow_run |
-| .github/workflows/artifactpoisoning42.yml:22:14:22:18 | ./cmd | .github/workflows/artifactpoisoning42.yml:13:9:21:6 | Run Step | .github/workflows/artifactpoisoning42.yml:22:14:22:18 | ./cmd | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/artifactpoisoning42.yml:22:14:22:18 | ./cmd | ./cmd | .github/workflows/artifactpoisoning42.yml:4:3:4:14 | workflow_run | workflow_run |
-| .github/workflows/artifactpoisoning71.yml:17:14:18:40 | sed -f config foo.md > bar.md\n | .github/workflows/artifactpoisoning71.yml:9:9:16:6 | Uses Step | .github/workflows/artifactpoisoning71.yml:17:14:18:40 | sed -f config foo.md > bar.md\n | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/artifactpoisoning71.yml:17:14:18:40 | sed -f config foo.md > bar.md\n | sed -f config foo.md > bar.md\n | .github/workflows/artifactpoisoning71.yml:4:5:4:16 | workflow_run | workflow_run |
-| .github/workflows/artifactpoisoning81.yml:31:14:31:27 | python test.py | .github/workflows/artifactpoisoning81.yml:28:9:31:6 | Uses Step | .github/workflows/artifactpoisoning81.yml:31:14:31:27 | python test.py | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/artifactpoisoning81.yml:31:14:31:27 | python test.py | python test.py | .github/workflows/artifactpoisoning81.yml:3:5:3:23 | pull_request_target | pull_request_target |
-| .github/workflows/artifactpoisoning92.yml:28:9:29:6 | Uses Step | .github/actions/download-artifact-2/action.yaml:6:7:25:4 | Uses Step | .github/workflows/artifactpoisoning92.yml:28:9:29:6 | Uses Step | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/artifactpoisoning92.yml:28:9:29:6 | Uses Step | Uses Step | .github/workflows/artifactpoisoning92.yml:3:3:3:14 | workflow_run | workflow_run |
-| .github/workflows/artifactpoisoning92.yml:29:14:29:26 | make snapshot | .github/actions/download-artifact-2/action.yaml:6:7:25:4 | Uses Step | .github/workflows/artifactpoisoning92.yml:29:14:29:26 | make snapshot | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/artifactpoisoning92.yml:29:14:29:26 | make snapshot | make snapshot | .github/workflows/artifactpoisoning92.yml:3:3:3:14 | workflow_run | workflow_run |
-| .github/workflows/artifactpoisoning96.yml:18:14:18:24 | npm install | .github/workflows/artifactpoisoning96.yml:13:9:18:6 | Uses Step | .github/workflows/artifactpoisoning96.yml:18:14:18:24 | npm install | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/artifactpoisoning96.yml:18:14:18:24 | npm install | npm install | .github/workflows/artifactpoisoning96.yml:2:3:2:14 | workflow_run | workflow_run |
-| .github/workflows/artifactpoisoning101.yml:17:14:19:59 | PR_NUMBER=$(./get_pull_request_number.sh pr_number.txt)\necho "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT \n | .github/workflows/artifactpoisoning101.yml:10:9:16:6 | Uses Step | .github/workflows/artifactpoisoning101.yml:17:14:19:59 | PR_NUMBER=$(./get_pull_request_number.sh pr_number.txt)\necho "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT \n | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/artifactpoisoning101.yml:17:14:19:59 | PR_NUMBER=$(./get_pull_request_number.sh pr_number.txt)\necho "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT \n | PR_NUMBER=$(./get_pull_request_number.sh pr_number.txt)\necho "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT \n | .github/workflows/artifactpoisoning101.yml:4:3:4:21 | pull_request_target | pull_request_target |
-| .github/workflows/test18.yml:36:15:40:58 | Uses Step | .github/workflows/test18.yml:12:15:33:12 | Uses Step | .github/workflows/test18.yml:36:15:40:58 | Uses Step | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/test18.yml:36:15:40:58 | Uses Step | Uses Step | .github/workflows/test18.yml:3:5:3:16 | workflow_run | workflow_run |
-| .github/workflows/test25.yml:39:14:40:45 | ./gradlew buildScanPublishPrevious\n | .github/workflows/test25.yml:22:9:32:6 | Uses Step: downloadBuildScan | .github/workflows/test25.yml:39:14:40:45 | ./gradlew buildScanPublishPrevious\n | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/test25.yml:39:14:40:45 | ./gradlew buildScanPublishPrevious\n | ./gradlew buildScanPublishPrevious\n | .github/workflows/test25.yml:2:3:2:14 | workflow_run | workflow_run |
+| .github/actions/download-artifact-2/action.yaml:6:7:25:4 | Uses Step | .github/actions/download-artifact-2/action.yaml:6:7:25:4 | Uses Step | .github/workflows/artifactpoisoning92.yml:28:9:29:6 | Uses Step | Potential artifact poisoning; the artifact being consumed has contents that may be controlled by an external user ($@). | .github/workflows/artifactpoisoning92.yml:3:3:3:14 | workflow_run | workflow_run |
+| .github/actions/download-artifact-2/action.yaml:6:7:25:4 | Uses Step | .github/actions/download-artifact-2/action.yaml:6:7:25:4 | Uses Step | .github/workflows/artifactpoisoning92.yml:29:14:29:26 | make snapshot | Potential artifact poisoning; the artifact being consumed has contents that may be controlled by an external user ($@). | .github/workflows/artifactpoisoning92.yml:3:3:3:14 | workflow_run | workflow_run |
+| .github/workflows/artifactpoisoning11.yml:13:9:32:6 | Uses Step | .github/workflows/artifactpoisoning11.yml:13:9:32:6 | Uses Step | .github/workflows/artifactpoisoning11.yml:38:11:38:77 | ./sonarcloud-data/x.py build -j$(nproc) --compiler gcc --skip-build | Potential artifact poisoning; the artifact being consumed has contents that may be controlled by an external user ($@). | .github/workflows/artifactpoisoning11.yml:4:3:4:14 | workflow_run | workflow_run |
+| .github/workflows/artifactpoisoning12.yml:13:9:32:6 | Uses Step | .github/workflows/artifactpoisoning12.yml:13:9:32:6 | Uses Step | .github/workflows/artifactpoisoning12.yml:38:11:38:25 | python foo/x.py | Potential artifact poisoning; the artifact being consumed has contents that may be controlled by an external user ($@). | .github/workflows/artifactpoisoning12.yml:4:3:4:14 | workflow_run | workflow_run |
+| .github/workflows/artifactpoisoning21.yml:13:9:18:6 | Uses Step | .github/workflows/artifactpoisoning21.yml:13:9:18:6 | Uses Step | .github/workflows/artifactpoisoning21.yml:19:14:20:21 | sh foo/cmd\n | Potential artifact poisoning; the artifact being consumed has contents that may be controlled by an external user ($@). | .github/workflows/artifactpoisoning21.yml:4:3:4:14 | workflow_run | workflow_run |
+| .github/workflows/artifactpoisoning22.yml:13:9:17:6 | Uses Step | .github/workflows/artifactpoisoning22.yml:13:9:17:6 | Uses Step | .github/workflows/artifactpoisoning22.yml:18:14:18:19 | sh cmd | Potential artifact poisoning; the artifact being consumed has contents that may be controlled by an external user ($@). | .github/workflows/artifactpoisoning22.yml:4:3:4:14 | workflow_run | workflow_run |
+| .github/workflows/artifactpoisoning31.yml:13:9:15:6 | Run Step | .github/workflows/artifactpoisoning31.yml:13:9:15:6 | Run Step | .github/workflows/artifactpoisoning31.yml:19:14:19:22 | ./foo/cmd | Potential artifact poisoning; the artifact being consumed has contents that may be controlled by an external user ($@). | .github/workflows/artifactpoisoning31.yml:4:3:4:14 | workflow_run | workflow_run |
+| .github/workflows/artifactpoisoning32.yml:13:9:16:6 | Run Step | .github/workflows/artifactpoisoning32.yml:13:9:16:6 | Run Step | .github/workflows/artifactpoisoning32.yml:17:14:18:20 | ./bar/cmd\n | Potential artifact poisoning; the artifact being consumed has contents that may be controlled by an external user ($@). | .github/workflows/artifactpoisoning32.yml:4:3:4:14 | workflow_run | workflow_run |
+| .github/workflows/artifactpoisoning33.yml:13:9:16:6 | Run Step | .github/workflows/artifactpoisoning33.yml:13:9:16:6 | Run Step | .github/workflows/artifactpoisoning33.yml:17:14:18:20 | ./bar/cmd\n | Potential artifact poisoning; the artifact being consumed has contents that may be controlled by an external user ($@). | .github/workflows/artifactpoisoning33.yml:4:3:4:14 | workflow_run | workflow_run |
+| .github/workflows/artifactpoisoning34.yml:13:9:16:6 | Run Step | .github/workflows/artifactpoisoning34.yml:13:9:16:6 | Run Step | .github/workflows/artifactpoisoning34.yml:20:14:22:23 | npm install\nnpm run lint\n | Potential artifact poisoning; the artifact being consumed has contents that may be controlled by an external user ($@). | .github/workflows/artifactpoisoning34.yml:4:3:4:14 | workflow_run | workflow_run |
+| .github/workflows/artifactpoisoning41.yml:13:9:21:6 | Run Step | .github/workflows/artifactpoisoning41.yml:13:9:21:6 | Run Step | .github/workflows/artifactpoisoning41.yml:22:14:22:22 | ./foo/cmd | Potential artifact poisoning; the artifact being consumed has contents that may be controlled by an external user ($@). | .github/workflows/artifactpoisoning41.yml:4:3:4:14 | workflow_run | workflow_run |
+| .github/workflows/artifactpoisoning42.yml:13:9:21:6 | Run Step | .github/workflows/artifactpoisoning42.yml:13:9:21:6 | Run Step | .github/workflows/artifactpoisoning42.yml:22:14:22:18 | ./cmd | Potential artifact poisoning; the artifact being consumed has contents that may be controlled by an external user ($@). | .github/workflows/artifactpoisoning42.yml:4:3:4:14 | workflow_run | workflow_run |
+| .github/workflows/artifactpoisoning71.yml:9:9:16:6 | Uses Step | .github/workflows/artifactpoisoning71.yml:9:9:16:6 | Uses Step | .github/workflows/artifactpoisoning71.yml:17:14:18:40 | sed -f config foo.md > bar.md\n | Potential artifact poisoning; the artifact being consumed has contents that may be controlled by an external user ($@). | .github/workflows/artifactpoisoning71.yml:4:5:4:16 | workflow_run | workflow_run |
+| .github/workflows/artifactpoisoning81.yml:28:9:31:6 | Uses Step | .github/workflows/artifactpoisoning81.yml:28:9:31:6 | Uses Step | .github/workflows/artifactpoisoning81.yml:31:14:31:27 | python test.py | Potential artifact poisoning; the artifact being consumed has contents that may be controlled by an external user ($@). | .github/workflows/artifactpoisoning81.yml:3:5:3:23 | pull_request_target | pull_request_target |
+| .github/workflows/artifactpoisoning96.yml:13:9:18:6 | Uses Step | .github/workflows/artifactpoisoning96.yml:13:9:18:6 | Uses Step | .github/workflows/artifactpoisoning96.yml:18:14:18:24 | npm install | Potential artifact poisoning; the artifact being consumed has contents that may be controlled by an external user ($@). | .github/workflows/artifactpoisoning96.yml:2:3:2:14 | workflow_run | workflow_run |
+| .github/workflows/artifactpoisoning101.yml:10:9:16:6 | Uses Step | .github/workflows/artifactpoisoning101.yml:10:9:16:6 | Uses Step | .github/workflows/artifactpoisoning101.yml:17:14:19:59 | PR_NUMBER=$(./get_pull_request_number.sh pr_number.txt)\necho "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT \n | Potential artifact poisoning; the artifact being consumed has contents that may be controlled by an external user ($@). | .github/workflows/artifactpoisoning101.yml:4:3:4:21 | pull_request_target | pull_request_target |
+| .github/workflows/test18.yml:12:15:33:12 | Uses Step | .github/workflows/test18.yml:12:15:33:12 | Uses Step | .github/workflows/test18.yml:36:15:40:58 | Uses Step | Potential artifact poisoning; the artifact being consumed has contents that may be controlled by an external user ($@). | .github/workflows/test18.yml:3:5:3:16 | workflow_run | workflow_run |
+| .github/workflows/test25.yml:22:9:32:6 | Uses Step: downloadBuildScan | .github/workflows/test25.yml:22:9:32:6 | Uses Step: downloadBuildScan | .github/workflows/test25.yml:39:14:40:45 | ./gradlew buildScanPublishPrevious\n | Potential artifact poisoning; the artifact being consumed has contents that may be controlled by an external user ($@). | .github/workflows/test25.yml:2:3:2:14 | workflow_run | workflow_run |
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 57d240fd7958..d4b805999500 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,10 +7,12 @@ 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
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
@@ -28,6 +30,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
@@ -40,6 +43,7 @@ ql/cpp/ql/src/Security/CWE/CWE-367/TOCTOUFilesystemRace.ql
ql/cpp/ql/src/Security/CWE/CWE-416/IteratorToExpiredContainer.ql
ql/cpp/ql/src/Security/CWE/CWE-416/UseOfStringAfterLifetimeEnds.ql
ql/cpp/ql/src/Security/CWE/CWE-416/UseOfUniquePointerAfterLifetimeEnds.ql
+ql/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql
ql/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql
ql/cpp/ql/src/Security/CWE/CWE-611/XXE.ql
ql/cpp/ql/src/Security/CWE/CWE-676/DangerousFunctionOverflow.ql
diff --git a/cpp/ql/lib/CHANGELOG.md b/cpp/ql/lib/CHANGELOG.md
index 686195e9211e..2cd1bcede35e 100644
--- a/cpp/ql/lib/CHANGELOG.md
+++ b/cpp/ql/lib/CHANGELOG.md
@@ -1,3 +1,34 @@
+## 10.0.0
+
+### Breaking Changes
+
+* The deprecated `NonThrowingFunction` class has been removed, use `NonCppThrowingFunction` instead.
+* The deprecated `ThrowingFunction` class has been removed, use `AlwaysSehThrowingFunction` instead.
+
+### New Features
+
+* Added a subclass `AutoconfConfigureTestFile` of `ConfigurationTestFile` that represents files created by GNU autoconf configure scripts to test the build configuration.
+
+## 9.0.0
+
+### Breaking Changes
+
+* The `SourceModelCsv`, `SinkModelCsv`, and `SummaryModelCsv` classes and the associated CSV parsing infrastructure have been removed from `ExternalFlow.qll`. New models should be added as `.model.yml` files in the `ext/` directory.
+
+### New Features
+
+* Added a subclass `MesonPrivateTestFile` of `ConfigurationTestFile` that represents files created by Meson to test the build configuration.
+* Added a class `ConstructorDirectFieldInit` to represent field initializations that occur in member initializer lists.
+* Added a class `ConstructorDefaultFieldInit` to represent default field initializations.
+* Added a class `DataFlow::IndirectParameterNode` to represent the indirection of a parameter as a dataflow node.
+* Added a predicate `Node::asIndirectInstruction` which returns the `Instruction` that defines the indirect dataflow node, if any.
+* Added a class `IndirectUninitializedNode` to represent the indirection of an uninitialized local variable as a dataflow node.
+
+### Minor Analysis Improvements
+
+* Added `HttpReceiveHttpRequest`, `HttpReceiveRequestEntityBody`, and `HttpReceiveClientCertificate` from Win32's `http.h` as remote flow sources.
+* Added dataflow through members initialized via non-static data member initialization (NSDMI).
+
## 8.0.3
No user-facing changes.
diff --git a/cpp/ql/lib/change-notes/2026-03-20-add-indirect-uninitialized-node.md b/cpp/ql/lib/change-notes/2026-03-20-add-indirect-uninitialized-node.md
deleted file mode 100644
index 07235e047d43..000000000000
--- a/cpp/ql/lib/change-notes/2026-03-20-add-indirect-uninitialized-node.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: feature
----
-* Added a class `IndirectUninitializedNode` to represent the indirection of an uninitialized local variable as a dataflow node.
diff --git a/cpp/ql/lib/change-notes/2026-03-23-indirect-parameter-nodes-and-indirect-instructions.md b/cpp/ql/lib/change-notes/2026-03-23-indirect-parameter-nodes-and-indirect-instructions.md
deleted file mode 100644
index c3bd4028ee97..000000000000
--- a/cpp/ql/lib/change-notes/2026-03-23-indirect-parameter-nodes-and-indirect-instructions.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-category: feature
----
-* Added a class `DataFlow::IndirectParameterNode` to represent the indirection of a parameter as a dataflow node.
-* Added a predicate `Node::asIndirectInstruction` which returns the `Instruction` that defines the indirect dataflow node, if any.
\ No newline at end of file
diff --git a/cpp/ql/lib/change-notes/2026-03-24-field-init.md b/cpp/ql/lib/change-notes/2026-03-24-field-init.md
deleted file mode 100644
index c11329a3d9f0..000000000000
--- a/cpp/ql/lib/change-notes/2026-03-24-field-init.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-category: feature
----
-* Added a class `ConstructorDirectFieldInit` to represent field initializations that occur in member initializer lists.
-* Added a class `ConstructorDefaultFieldInit` to represent default field initializations.
diff --git a/cpp/ql/lib/change-notes/2026-03-26-convert-csv-models-to-yml.md b/cpp/ql/lib/change-notes/2026-03-26-convert-csv-models-to-yml.md
deleted file mode 100644
index 41d77b518f14..000000000000
--- a/cpp/ql/lib/change-notes/2026-03-26-convert-csv-models-to-yml.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: breaking
----
-* The `SourceModelCsv`, `SinkModelCsv`, and `SummaryModelCsv` classes and the associated CSV parsing infrastructure have been removed from `ExternalFlow.qll`. New models should be added as `.model.yml` files in the `ext/` directory.
diff --git a/cpp/ql/lib/change-notes/2026-03-30-nsdmi-dataflow.md b/cpp/ql/lib/change-notes/2026-03-30-nsdmi-dataflow.md
deleted file mode 100644
index 8bf879003304..000000000000
--- a/cpp/ql/lib/change-notes/2026-03-30-nsdmi-dataflow.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: minorAnalysis
----
-* Added dataflow through members initialized via non-static data member initialization (NSDMI).
\ No newline at end of file
diff --git a/cpp/ql/lib/change-notes/2026-03-31-http-flow-sources.md b/cpp/ql/lib/change-notes/2026-03-31-http-flow-sources.md
deleted file mode 100644
index 54a0ad81036d..000000000000
--- a/cpp/ql/lib/change-notes/2026-03-31-http-flow-sources.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: minorAnalysis
----
-* Added `HttpReceiveHttpRequest`, `HttpReceiveRequestEntityBody`, and `HttpReceiveClientCertificate` from Win32's `http.h` as remote flow sources.
\ No newline at end of file
diff --git a/cpp/ql/lib/change-notes/2026-03-31-meson.md b/cpp/ql/lib/change-notes/2026-03-31-meson.md
deleted file mode 100644
index c18de40b85db..000000000000
--- a/cpp/ql/lib/change-notes/2026-03-31-meson.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: feature
----
-* Added a subclass `MesonPrivateTestFile` of `ConfigurationTestFile` that represents files created by Meson to test the build configuration.
diff --git a/cpp/ql/lib/change-notes/2026-04-07-autoconf.md b/cpp/ql/lib/change-notes/2026-04-07-autoconf.md
deleted file mode 100644
index 9f04417b8e25..000000000000
--- a/cpp/ql/lib/change-notes/2026-04-07-autoconf.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: feature
----
-* Added a subclass `AutoconfConfigureTestFile` of `ConfigurationTestFile` that represents files created by GNU autoconf configure scripts to test the build configuration.
diff --git a/cpp/ql/lib/change-notes/released/10.0.0.md b/cpp/ql/lib/change-notes/released/10.0.0.md
new file mode 100644
index 000000000000..af591bd1a0ad
--- /dev/null
+++ b/cpp/ql/lib/change-notes/released/10.0.0.md
@@ -0,0 +1,10 @@
+## 10.0.0
+
+### Breaking Changes
+
+* The deprecated `NonThrowingFunction` class has been removed, use `NonCppThrowingFunction` instead.
+* The deprecated `ThrowingFunction` class has been removed, use `AlwaysSehThrowingFunction` instead.
+
+### New Features
+
+* Added a subclass `AutoconfConfigureTestFile` of `ConfigurationTestFile` that represents files created by GNU autoconf configure scripts to test the build configuration.
diff --git a/cpp/ql/lib/change-notes/released/9.0.0.md b/cpp/ql/lib/change-notes/released/9.0.0.md
new file mode 100644
index 000000000000..2f97209a02d2
--- /dev/null
+++ b/cpp/ql/lib/change-notes/released/9.0.0.md
@@ -0,0 +1,19 @@
+## 9.0.0
+
+### Breaking Changes
+
+* The `SourceModelCsv`, `SinkModelCsv`, and `SummaryModelCsv` classes and the associated CSV parsing infrastructure have been removed from `ExternalFlow.qll`. New models should be added as `.model.yml` files in the `ext/` directory.
+
+### New Features
+
+* Added a subclass `MesonPrivateTestFile` of `ConfigurationTestFile` that represents files created by Meson to test the build configuration.
+* Added a class `ConstructorDirectFieldInit` to represent field initializations that occur in member initializer lists.
+* Added a class `ConstructorDefaultFieldInit` to represent default field initializations.
+* Added a class `DataFlow::IndirectParameterNode` to represent the indirection of a parameter as a dataflow node.
+* Added a predicate `Node::asIndirectInstruction` which returns the `Instruction` that defines the indirect dataflow node, if any.
+* Added a class `IndirectUninitializedNode` to represent the indirection of an uninitialized local variable as a dataflow node.
+
+### Minor Analysis Improvements
+
+* Added `HttpReceiveHttpRequest`, `HttpReceiveRequestEntityBody`, and `HttpReceiveClientCertificate` from Win32's `http.h` as remote flow sources.
+* Added dataflow through members initialized via non-static data member initialization (NSDMI).
diff --git a/cpp/ql/lib/codeql-pack.release.yml b/cpp/ql/lib/codeql-pack.release.yml
index 1be4ac8d0d39..28758256b943 100644
--- a/cpp/ql/lib/codeql-pack.release.yml
+++ b/cpp/ql/lib/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 8.0.3
+lastReleaseVersion: 10.0.0
diff --git a/cpp/ql/lib/ext/allocation/Std.allocation.model.yml b/cpp/ql/lib/ext/allocation/Std.allocation.model.yml
index 16b3d5bcebad..227cc4176c0d 100644
--- a/cpp/ql/lib/ext/allocation/Std.allocation.model.yml
+++ b/cpp/ql/lib/ext/allocation/Std.allocation.model.yml
@@ -12,4 +12,7 @@ extensions:
- ["", "", False, "_malloca", "0", "", "", False]
- ["", "", False, "calloc", "1", "0", "", True]
- ["std", "", False, "calloc", "1", "0", "", True]
- - ["bsl", "", False, "calloc", "1", "0", "", True]
\ No newline at end of file
+ - ["bsl", "", False, "calloc", "1", "0", "", True]
+ - ["", "", False, "aligned_alloc", "1", "", "", True]
+ - ["std", "", False, "aligned_alloc", "1", "", "", True]
+ - ["bsl", "", False, "aligned_alloc", "1", "", "", True]
diff --git a/cpp/ql/lib/qlpack.yml b/cpp/ql/lib/qlpack.yml
index 802f3c3e4de8..8a9d60a7fa94 100644
--- a/cpp/ql/lib/qlpack.yml
+++ b/cpp/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/cpp-all
-version: 8.0.4-dev
+version: 10.0.1-dev
groups: cpp
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp
diff --git a/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll b/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll
index d189dd36f87c..624465761c2c 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,22 +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(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 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
@@ -554,12 +559,10 @@ 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 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 f032ba4749e6..98280a522cfd 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,16 +234,14 @@ 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 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)
)
)
}
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll
index 8b71f140b01b..fb2108c2ac58 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll
@@ -6,11 +6,15 @@
*
* 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:
+ * `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.
@@ -87,11 +91,23 @@
* 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 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
* 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
@@ -931,13 +947,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
)
}
@@ -954,14 +970,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 1a572c221d9f..22c74c2aa714 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 cce1b80e7fcb..d91dc41febeb 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/cpp/ql/lib/semmle/code/cpp/models/interfaces/NonThrowing.qll b/cpp/ql/lib/semmle/code/cpp/models/interfaces/NonThrowing.qll
index 85b9b66cd661..04826a487ca7 100644
--- a/cpp/ql/lib/semmle/code/cpp/models/interfaces/NonThrowing.qll
+++ b/cpp/ql/lib/semmle/code/cpp/models/interfaces/NonThrowing.qll
@@ -11,10 +11,3 @@ import semmle.code.cpp.models.Models
* The function may still raise a structured exception handling (SEH) exception.
*/
abstract class NonCppThrowingFunction extends Function { }
-
-/**
- * A function that is guaranteed to never throw.
- *
- * DEPRECATED: use `NonCppThrowingFunction` instead.
- */
-deprecated class NonThrowingFunction = NonCppThrowingFunction;
diff --git a/cpp/ql/lib/semmle/code/cpp/models/interfaces/Throwing.qll b/cpp/ql/lib/semmle/code/cpp/models/interfaces/Throwing.qll
index 111b99533957..a781bab07c35 100644
--- a/cpp/ql/lib/semmle/code/cpp/models/interfaces/Throwing.qll
+++ b/cpp/ql/lib/semmle/code/cpp/models/interfaces/Throwing.qll
@@ -10,19 +10,6 @@ import semmle.code.cpp.Function
import semmle.code.cpp.models.Models
import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs
-/**
- * A function that is known to raise an exception.
- *
- * DEPRECATED: use `AlwaysSehThrowingFunction` instead.
- */
-abstract deprecated class ThrowingFunction extends Function {
- /**
- * Holds if this function may throw an exception during evaluation.
- * If `unconditional` is `true` the function always throws an exception.
- */
- abstract predicate mayThrowException(boolean unconditional);
-}
-
/**
* A function that unconditionally raises a structured exception handling (SEH) exception.
*/
diff --git a/cpp/ql/src/CHANGELOG.md b/cpp/ql/src/CHANGELOG.md
index 126b1d9efa34..80b9ad0e4753 100644
--- a/cpp/ql/src/CHANGELOG.md
+++ b/cpp/ql/src/CHANGELOG.md
@@ -1,3 +1,28 @@
+## 1.6.1
+
+### Minor Analysis Improvements
+
+* Added `AllocationFunction` models for `aligned_alloc`, `std::aligned_alloc`, and `bsl::aligned_alloc`.
+* 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.
+* 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.
+* The "Suspicious add with sizeof" (`cpp/suspicious-add-sizeof`) query has been upgraded to `high` precision. This query will now run in the default code scanning suite.
+* 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.
+* The "Implicit function declaration" (`cpp/implicit-function-declaration`) query has been upgraded to `high` precision. However, for `build mode: none` databases, it no longer produces any results. The results in this mode were found to be very noisy and fundamentally imprecise.
+
+## 1.6.0
+
+### Query Metadata Changes
+
+* The `@security-severity` metadata of `cpp/cgi-xss` has been increased from 6.1 (medium) to 7.8 (high).
+
+### Minor Analysis Improvements
+
+* The "Extraction warnings" (`cpp/diagnostics/extraction-warnings`) diagnostics query no longer yields `ExtractionRecoverableWarning`s for `build-mode: none` databases. The results were found to significantly increase the sizes of the produced SARIF files, making them unprocessable in some cases.
+* Fixed an issue with the "Suspicious add with sizeof" (`cpp/suspicious-add-sizeof`) query causing false positive results in `build-mode: none` databases.
+* Fixed an issue with the "Uncontrolled format string" (`cpp/tainted-format-string`) query involving certain kinds of formatting function implementations.
+* Fixed an issue with the "Wrong type of arguments to formatting function" (`cpp/wrong-type-format-argument`) query causing false positive results in `build-mode: none` databases.
+* Fixed an issue with the "Multiplication result converted to larger type" (`cpp/integer-multiplication-cast-to-long`) query causing false positive results in `build-mode: none` databases.
+
## 1.5.15
No user-facing changes.
@@ -341,7 +366,7 @@ No user-facing changes.
### Minor Analysis Improvements
* The "non-constant format string" query (`cpp/non-constant-format`) has been updated to produce fewer false positives.
-* Added dataflow models for the `gettext` function variants.
+* Added dataflow models for the `gettext` function variants.
## 0.9.4
diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql b/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql
index 6747d177c80e..b05bd637dc2d 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
diff --git a/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql b/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql
index 7f0a4833cb59..5842b9474f74 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
diff --git a/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.qhelp b/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.qhelp
index 6ff60d383419..90a98e1bf573 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.
-
\ No newline at end of file
+
diff --git a/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql b/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.ql
index 6a55557cf70b..00b29efbd0f2 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
@@ -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 produces
+ * 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
diff --git a/cpp/ql/src/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.qll b/cpp/ql/src/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.qll
index 2dced5d8d844..dbb457db505e 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 218a54b36c51..fd323513a49e 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 7fba78b5550e..ab2a98ae3a55 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) {
diff --git a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql
index 3f330807304f..7d9ef88adea1 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
diff --git a/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql b/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql
index 343e96a00d39..d5a5cd8f6655 100644
--- a/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql
+++ b/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql
@@ -6,7 +6,7 @@
* @kind problem
* @problem.severity warning
* @security-severity 8.8
- * @precision medium
+ * @precision high
* @id cpp/suspicious-add-sizeof
* @tags security
* external/cwe/cwe-468
diff --git a/cpp/ql/src/change-notes/2026-03-11-integer-multiplication-cast-to-long.md b/cpp/ql/src/change-notes/2026-03-11-integer-multiplication-cast-to-long.md
deleted file mode 100644
index 4d4a66c0a226..000000000000
--- a/cpp/ql/src/change-notes/2026-03-11-integer-multiplication-cast-to-long.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: minorAnalysis
----
-* Fixed an issue with the "Multiplication result converted to larger type" (`cpp/integer-multiplication-cast-to-long`) query causing false positive results in `build-mode: none` databases.
diff --git a/cpp/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md b/cpp/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md
deleted file mode 100644
index 0810e9c49bac..000000000000
--- a/cpp/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: queryMetadata
----
-* The `@security-severity` metadata of `cpp/cgi-xss` has been increased from 6.1 (medium) to 7.8 (high).
diff --git a/cpp/ql/src/change-notes/2026-03-16-wrong-type-format-argument.md b/cpp/ql/src/change-notes/2026-03-16-wrong-type-format-argument.md
deleted file mode 100644
index 84aef7791fcf..000000000000
--- a/cpp/ql/src/change-notes/2026-03-16-wrong-type-format-argument.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: minorAnalysis
----
-* Fixed an issue with the "Wrong type of arguments to formatting function" (`cpp/wrong-type-format-argument`) query causing false positive results in `build-mode: none` databases.
diff --git a/cpp/ql/src/change-notes/2026-03-19-suspicious-add-sizeof.md b/cpp/ql/src/change-notes/2026-03-19-suspicious-add-sizeof.md
deleted file mode 100644
index 387e2d44b469..000000000000
--- a/cpp/ql/src/change-notes/2026-03-19-suspicious-add-sizeof.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: minorAnalysis
----
-* Fixed an issue with the "Suspicious add with sizeof" (`cpp/suspicious-add-sizeof`) query causing false positive results in `build-mode: none` databases.
diff --git a/cpp/ql/src/change-notes/2026-03-19-tainted-format-string.md b/cpp/ql/src/change-notes/2026-03-19-tainted-format-string.md
deleted file mode 100644
index 6a1133917bf7..000000000000
--- a/cpp/ql/src/change-notes/2026-03-19-tainted-format-string.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: minorAnalysis
----
-* Fixed an issue with the "Uncontrolled format string" (`cpp/tainted-format-string`) query involving certain kinds of formatting function implementations.
diff --git a/cpp/ql/src/change-notes/2026-03-30-warning-diagnostics.md b/cpp/ql/src/change-notes/2026-03-30-warning-diagnostics.md
deleted file mode 100644
index 0db0d7c718da..000000000000
--- a/cpp/ql/src/change-notes/2026-03-30-warning-diagnostics.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: minorAnalysis
----
-* The "Extraction warnings" (`cpp/diagnostics/extraction-warnings`) diagnostics query no longer yields `ExtractionRecoverableWarning`s for `build-mode: none` databases. The results were found to significantly increase the sizes of the produced SARIF files, making them unprocessable in some cases.
diff --git a/cpp/ql/src/change-notes/released/1.6.0.md b/cpp/ql/src/change-notes/released/1.6.0.md
new file mode 100644
index 000000000000..3bbb94806609
--- /dev/null
+++ b/cpp/ql/src/change-notes/released/1.6.0.md
@@ -0,0 +1,13 @@
+## 1.6.0
+
+### Query Metadata Changes
+
+* The `@security-severity` metadata of `cpp/cgi-xss` has been increased from 6.1 (medium) to 7.8 (high).
+
+### Minor Analysis Improvements
+
+* The "Extraction warnings" (`cpp/diagnostics/extraction-warnings`) diagnostics query no longer yields `ExtractionRecoverableWarning`s for `build-mode: none` databases. The results were found to significantly increase the sizes of the produced SARIF files, making them unprocessable in some cases.
+* Fixed an issue with the "Suspicious add with sizeof" (`cpp/suspicious-add-sizeof`) query causing false positive results in `build-mode: none` databases.
+* Fixed an issue with the "Uncontrolled format string" (`cpp/tainted-format-string`) query involving certain kinds of formatting function implementations.
+* Fixed an issue with the "Wrong type of arguments to formatting function" (`cpp/wrong-type-format-argument`) query causing false positive results in `build-mode: none` databases.
+* Fixed an issue with the "Multiplication result converted to larger type" (`cpp/integer-multiplication-cast-to-long`) query causing false positive results in `build-mode: none` databases.
diff --git a/cpp/ql/src/change-notes/released/1.6.1.md b/cpp/ql/src/change-notes/released/1.6.1.md
new file mode 100644
index 000000000000..83781b87c584
--- /dev/null
+++ b/cpp/ql/src/change-notes/released/1.6.1.md
@@ -0,0 +1,10 @@
+## 1.6.1
+
+### Minor Analysis Improvements
+
+* Added `AllocationFunction` models for `aligned_alloc`, `std::aligned_alloc`, and `bsl::aligned_alloc`.
+* 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.
+* 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.
+* The "Suspicious add with sizeof" (`cpp/suspicious-add-sizeof`) query has been upgraded to `high` precision. This query will now run in the default code scanning suite.
+* 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.
+* The "Implicit function declaration" (`cpp/implicit-function-declaration`) query has been upgraded to `high` precision. However, for `build mode: none` databases, it no longer produces any results. The results in this mode were found to be very noisy and fundamentally imprecise.
diff --git a/cpp/ql/src/codeql-pack.release.yml b/cpp/ql/src/codeql-pack.release.yml
index b41e6e78a66a..ef7a789e0cf1 100644
--- a/cpp/ql/src/codeql-pack.release.yml
+++ b/cpp/ql/src/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 1.5.15
+lastReleaseVersion: 1.6.1
diff --git a/cpp/ql/src/qlpack.yml b/cpp/ql/src/qlpack.yml
index 3160da2efb68..714167434c8a 100644
--- a/cpp/ql/src/qlpack.yml
+++ b/cpp/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/cpp-queries
-version: 1.5.16-dev
+version: 1.6.2-dev
groups:
- cpp
- queries
diff --git a/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md b/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md
index dfbc0f9f376d..166a94bd88df 100644
--- a/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md
+++ b/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 1.7.65
+
+No user-facing changes.
+
+## 1.7.64
+
+No user-facing changes.
+
## 1.7.63
No user-facing changes.
diff --git a/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.7.64.md b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.7.64.md
new file mode 100644
index 000000000000..47290bbbeb30
--- /dev/null
+++ b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.7.64.md
@@ -0,0 +1,3 @@
+## 1.7.64
+
+No user-facing changes.
diff --git a/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.7.65.md b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.7.65.md
new file mode 100644
index 000000000000..12bf5dad4b08
--- /dev/null
+++ b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.7.65.md
@@ -0,0 +1,3 @@
+## 1.7.65
+
+No user-facing changes.
diff --git a/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml b/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml
index 3d19252da0b4..bf581427d298 100644
--- a/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml
+++ b/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 1.7.63
+lastReleaseVersion: 1.7.65
diff --git a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml
index 972746e92550..9d0e0ffd4f96 100644
--- a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml
+++ b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-all
-version: 1.7.64-dev
+version: 1.7.66-dev
groups:
- csharp
- solorigate
diff --git a/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md b/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md
index dfbc0f9f376d..166a94bd88df 100644
--- a/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md
+++ b/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 1.7.65
+
+No user-facing changes.
+
+## 1.7.64
+
+No user-facing changes.
+
## 1.7.63
No user-facing changes.
diff --git a/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.7.64.md b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.7.64.md
new file mode 100644
index 000000000000..47290bbbeb30
--- /dev/null
+++ b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.7.64.md
@@ -0,0 +1,3 @@
+## 1.7.64
+
+No user-facing changes.
diff --git a/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.7.65.md b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.7.65.md
new file mode 100644
index 000000000000..12bf5dad4b08
--- /dev/null
+++ b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.7.65.md
@@ -0,0 +1,3 @@
+## 1.7.65
+
+No user-facing changes.
diff --git a/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml b/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml
index 3d19252da0b4..bf581427d298 100644
--- a/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml
+++ b/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 1.7.63
+lastReleaseVersion: 1.7.65
diff --git a/csharp/ql/campaigns/Solorigate/src/qlpack.yml b/csharp/ql/campaigns/Solorigate/src/qlpack.yml
index 7a4e4fff627d..f5203f4e4434 100644
--- a/csharp/ql/campaigns/Solorigate/src/qlpack.yml
+++ b/csharp/ql/campaigns/Solorigate/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-queries
-version: 1.7.64-dev
+version: 1.7.66-dev
groups:
- csharp
- solorigate
diff --git a/csharp/ql/examples/snippets/integer_literal.ql b/csharp/ql/examples/snippets/integer_literal.ql
index 4546c1d174ba..36791fc8c374 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/CHANGELOG.md b/csharp/ql/lib/CHANGELOG.md
index 7261891eed7d..32cd8f33c650 100644
--- a/csharp/ql/lib/CHANGELOG.md
+++ b/csharp/ql/lib/CHANGELOG.md
@@ -1,3 +1,19 @@
+## 5.5.0
+
+### Deprecated APIs
+
+* The predicates `get[L|R]Value` in the class `Assignment` have been deprecated. Use `get[Left|Right]Operand` instead.
+
+## 5.4.12
+
+### Minor Analysis Improvements
+
+* The extractor no longer synthesizes expanded forms of compound assignments. This may have a small impact on the results of queries that explicitly or implicitly rely on the expanded form of compound assignments.
+* The `cs/log-forging` query no longer treats arguments to extension methods with
+ source code on `ILogger` types as sinks. Instead, taint is tracked interprocedurally
+ through extension method bodies, reducing false positives when extension methods
+ sanitize input internally.
+
## 5.4.11
No user-facing changes.
diff --git a/csharp/ql/lib/Linq/Helpers.qll b/csharp/ql/lib/Linq/Helpers.qll
index 912b12b9457b..2a4d5c8c27a2 100644
--- a/csharp/ql/lib/Linq/Helpers.qll
+++ b/csharp/ql/lib/Linq/Helpers.qll
@@ -121,15 +121,17 @@ predicate missedOfTypeOpportunity(ForeachStmtEnumerable fes, LocalVariableDeclSt
/**
* Holds if `foreach` statement `fes` can be converted to a `.Select()` call.
* That is, the loop variable is accessed only in the first statement of the
- * block, the access is not a cast, and the first statement is a
- * local variable declaration statement `s`.
+ * block, the access is not a cast, the first statement is a
+ * local variable declaration statement `s`, and the initializer does not
+ * contain an `await` expression (since `Select` does not support async lambdas).
*/
predicate missedSelectOpportunity(ForeachStmtGenericEnumerable fes, LocalVariableDeclStmt s) {
s = firstStmt(fes) and
forex(VariableAccess va | va = fes.getVariable().getAnAccess() |
va = s.getAVariableDeclExpr().getAChildExpr*()
) and
- not s.getAVariableDeclExpr().getInitializer() instanceof Cast
+ not s.getAVariableDeclExpr().getInitializer() instanceof Cast and
+ not s.getAVariableDeclExpr().getInitializer().getAChildExpr*() instanceof AwaitExpr
}
/**
diff --git a/csharp/ql/lib/change-notes/2026-03-26-expanded-assignments.md b/csharp/ql/lib/change-notes/2026-03-26-expanded-assignments.md
deleted file mode 100644
index 159ab1ee3c64..000000000000
--- a/csharp/ql/lib/change-notes/2026-03-26-expanded-assignments.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: minorAnalysis
----
-* The extractor no longer synthesizes expanded forms of compound assignments. This may have a small impact on the results of queries that explicitly or implicitly rely on the expanded form of compound assignments.
diff --git a/csharp/ql/lib/change-notes/2026-03-19-fix-log-forging-extension-methods.md b/csharp/ql/lib/change-notes/released/5.4.12.md
similarity index 52%
rename from csharp/ql/lib/change-notes/2026-03-19-fix-log-forging-extension-methods.md
rename to csharp/ql/lib/change-notes/released/5.4.12.md
index 65ce217b1058..506fae5a15e1 100644
--- a/csharp/ql/lib/change-notes/2026-03-19-fix-log-forging-extension-methods.md
+++ b/csharp/ql/lib/change-notes/released/5.4.12.md
@@ -1,6 +1,8 @@
----
-category: minorAnalysis
----
+## 5.4.12
+
+### Minor Analysis Improvements
+
+* The extractor no longer synthesizes expanded forms of compound assignments. This may have a small impact on the results of queries that explicitly or implicitly rely on the expanded form of compound assignments.
* The `cs/log-forging` query no longer treats arguments to extension methods with
source code on `ILogger` types as sinks. Instead, taint is tracked interprocedurally
through extension method bodies, reducing false positives when extension methods
diff --git a/csharp/ql/lib/change-notes/2026-04-01-getlrvalue.md b/csharp/ql/lib/change-notes/released/5.5.0.md
similarity index 79%
rename from csharp/ql/lib/change-notes/2026-04-01-getlrvalue.md
rename to csharp/ql/lib/change-notes/released/5.5.0.md
index da1a3d621481..b497d8ea51b4 100644
--- a/csharp/ql/lib/change-notes/2026-04-01-getlrvalue.md
+++ b/csharp/ql/lib/change-notes/released/5.5.0.md
@@ -1,4 +1,5 @@
----
-category: deprecated
----
+## 5.5.0
+
+### Deprecated APIs
+
* The predicates `get[L|R]Value` in the class `Assignment` have been deprecated. Use `get[Left|Right]Operand` instead.
diff --git a/csharp/ql/lib/codeql-pack.release.yml b/csharp/ql/lib/codeql-pack.release.yml
index f742ee59b537..4b8cf9533c17 100644
--- a/csharp/ql/lib/codeql-pack.release.yml
+++ b/csharp/ql/lib/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 5.4.11
+lastReleaseVersion: 5.5.0
diff --git a/csharp/ql/lib/qlpack.yml b/csharp/ql/lib/qlpack.yml
index b45bd57ad56e..7c906e033ad6 100644
--- a/csharp/ql/lib/qlpack.yml
+++ b/csharp/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/csharp-all
-version: 5.4.12-dev
+version: 5.5.1-dev
groups: csharp
dbscheme: semmlecode.csharp.dbscheme
extractor: csharp
diff --git a/csharp/ql/lib/semmle/code/csharp/Conversion.qll b/csharp/ql/lib/semmle/code/csharp/Conversion.qll
index ec7ef9cac952..e151944dc381 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]
@@ -718,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()])
@@ -735,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. */
@@ -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()
)
}
diff --git a/csharp/ql/lib/semmle/code/csharp/commons/ComparisonTest.qll b/csharp/ql/lib/semmle/code/csharp/commons/ComparisonTest.qll
index b4641560892b..776e2e97c370 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 5025202eb217..a5f1bc43abee 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/commons/Strings.qll b/csharp/ql/lib/semmle/code/csharp/commons/Strings.qll
index 678a1f928163..3fde913358b3 100644
--- a/csharp/ql/lib/semmle/code/csharp/commons/Strings.qll
+++ b/csharp/ql/lib/semmle/code/csharp/commons/Strings.qll
@@ -29,7 +29,7 @@ class ImplicitToStringExpr extends Expr {
m = p.getCallable()
|
m = any(SystemTextStringBuilderClass c).getAMethod() and
- m.getName().regexpMatch("Append(Line)?") and
+ m.getName() = "Append" and
not p.getType() instanceof ArrayType
or
p instanceof StringFormatItemParameter and
diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll
index 6c1eb8c06eb9..66b591cfcd25 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/DataFlowImplSpecific.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplSpecific.qll
index af104d777b87..ab1e75b3d0fc 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplSpecific.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplSpecific.qll
@@ -29,4 +29,8 @@ module CsharpDataFlow implements InputSig {
predicate neverSkipInPathGraph(Node n) {
exists(n.(AssignableDefinitionNode).getDefinition().getTargetAccess())
}
+
+ DataFlowType getSourceContextParameterNodeType(Node p) {
+ exists(p) and result.isSourceContextParameterType()
+ }
}
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
index 5554b8cdbd79..ae530b2d451a 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
@@ -1179,7 +1179,8 @@ private module Cached {
cached
newtype TDataFlowType =
TGvnDataFlowType(Gvn::GvnType t) or
- TDelegateDataFlowType(Callable lambda) { lambdaCreationExpr(_, lambda) }
+ TDelegateDataFlowType(Callable lambda) { lambdaCreationExpr(_, lambda) } or
+ TSourceContextParameterType()
}
import Cached
@@ -2394,6 +2395,8 @@ class DataFlowType extends TDataFlowType {
Callable asDelegate() { this = TDelegateDataFlowType(result) }
+ predicate isSourceContextParameterType() { this = TSourceContextParameterType() }
+
/**
* Gets an expression that creates a delegate of this type.
*
@@ -2412,6 +2415,9 @@ class DataFlowType extends TDataFlowType {
result = this.asGvnType().toString()
or
result = this.asDelegate().toString()
+ or
+ this.isSourceContextParameterType() and
+ result = ""
}
}
@@ -2469,6 +2475,11 @@ private predicate compatibleTypesDelegateLeft(DataFlowType dt1, DataFlowType dt2
)
}
+pragma[nomagic]
+private predicate compatibleTypesSourceContextParameterTypeLeft(DataFlowType dt1, DataFlowType dt2) {
+ dt1.isSourceContextParameterType() and not exists(dt2.asDelegate())
+}
+
/**
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
* a node of type `t1` to a node of type `t2`.
@@ -2499,6 +2510,10 @@ predicate compatibleTypes(DataFlowType dt1, DataFlowType dt2) {
compatibleTypesDelegateLeft(dt2, dt1)
or
dt1.asDelegate() = dt2.asDelegate()
+ or
+ compatibleTypesSourceContextParameterTypeLeft(dt1, dt2)
+ or
+ compatibleTypesSourceContextParameterTypeLeft(dt2, dt1)
}
pragma[nomagic]
@@ -2511,6 +2526,8 @@ predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) {
uselessTypebound(t2)
or
compatibleTypesDelegateLeft(t1, t2)
+ or
+ compatibleTypesSourceContextParameterTypeLeft(t1, t2)
}
/**
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 024e9cf119d5..f8cec8c4d9f6 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll
@@ -4,13 +4,17 @@
* 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:
* `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 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
* 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:
@@ -230,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."
)
}
@@ -482,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 3461f0a51863..cd438ece284d 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 ec6bbf81fee1..a7ab18a62901 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/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll
index e3f5deb98989..bea31ed7f551 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 36eda82531c5..a26afb004901 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 2c0ba292b9c5..aca2cb2cb1c3 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/CHANGELOG.md b/csharp/ql/src/CHANGELOG.md
index 39bfe944d5d6..cdab71341852 100644
--- a/csharp/ql/src/CHANGELOG.md
+++ b/csharp/ql/src/CHANGELOG.md
@@ -1,3 +1,23 @@
+## 1.7.1
+
+### Minor Analysis Improvements
+
+* The query `cs/useless-tostring-call` has been updated to avoid false
+ positive results in calls to `StringBuilder.AppendLine` and calls of
+ the form `base.ToString()`. Moreover, the alert message has been
+ made more precise.
+
+## 1.7.0
+
+### Query Metadata Changes
+
+* The `@security-severity` metadata of `cs/log-forging` has been reduced from 7.8 (high) to 6.1 (medium).
+* The `@security-severity` metadata of `cs/web/xss` has been increased from 6.1 (medium) to 7.8 (high).
+
+### Major Analysis Improvements
+
+* The `cs/constant-condition` query has been simplified. The query no longer reports trivially constant conditions as they were found to generally be intentional. As a result, it should now produce fewer false positives. Additionally, the simplification means that it now reports all the results that `cs/constant-comparison` used to report, and as consequence, that query has been deleted.
+
## 1.6.6
No user-facing changes.
diff --git a/csharp/ql/src/Likely Bugs/BadCheckOdd.ql b/csharp/ql/src/Likely Bugs/BadCheckOdd.ql
index 34ae4b632aec..72924f9103d8 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 c8df36bf7bf2..3e54a3a00dbd 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 8b719bb92a57..1c41d24fb3c1 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 02be9125835b..98a7852fbaf5 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."
}
diff --git a/csharp/ql/src/Useless code/RedundantToStringCall.ql b/csharp/ql/src/Useless code/RedundantToStringCall.ql
index 55e7056e9a08..e29e071b4d9b 100644
--- a/csharp/ql/src/Useless code/RedundantToStringCall.ql
+++ b/csharp/ql/src/Useless code/RedundantToStringCall.ql
@@ -18,5 +18,6 @@ import semmle.code.csharp.frameworks.System
from MethodCall mc
where
mc instanceof ImplicitToStringExpr and
- mc.getTarget() instanceof ToStringMethod
-select mc, "Redundant call to 'ToString' on a String object."
+ mc.getTarget() instanceof ToStringMethod and
+ not mc.getQualifier() instanceof BaseAccess
+select mc, "Redundant call to 'ToString'."
diff --git a/csharp/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md b/csharp/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md
deleted file mode 100644
index c317194bc259..000000000000
--- a/csharp/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-category: queryMetadata
----
-* The `@security-severity` metadata of `cs/log-forging` has been reduced from 7.8 (high) to 6.1 (medium).
-* The `@security-severity` metadata of `cs/web/xss` has been increased from 6.1 (medium) to 7.8 (high).
diff --git a/csharp/ql/src/change-notes/2026-03-31-constantcondition-simplify.md b/csharp/ql/src/change-notes/released/1.7.0.md
similarity index 58%
rename from csharp/ql/src/change-notes/2026-03-31-constantcondition-simplify.md
rename to csharp/ql/src/change-notes/released/1.7.0.md
index a1051d4c00f4..906a13d68d0a 100644
--- a/csharp/ql/src/change-notes/2026-03-31-constantcondition-simplify.md
+++ b/csharp/ql/src/change-notes/released/1.7.0.md
@@ -1,4 +1,10 @@
----
-category: majorAnalysis
----
+## 1.7.0
+
+### Query Metadata Changes
+
+* The `@security-severity` metadata of `cs/log-forging` has been reduced from 7.8 (high) to 6.1 (medium).
+* The `@security-severity` metadata of `cs/web/xss` has been increased from 6.1 (medium) to 7.8 (high).
+
+### Major Analysis Improvements
+
* The `cs/constant-condition` query has been simplified. The query no longer reports trivially constant conditions as they were found to generally be intentional. As a result, it should now produce fewer false positives. Additionally, the simplification means that it now reports all the results that `cs/constant-comparison` used to report, and as consequence, that query has been deleted.
diff --git a/csharp/ql/src/change-notes/released/1.7.1.md b/csharp/ql/src/change-notes/released/1.7.1.md
new file mode 100644
index 000000000000..0b5df9629c67
--- /dev/null
+++ b/csharp/ql/src/change-notes/released/1.7.1.md
@@ -0,0 +1,8 @@
+## 1.7.1
+
+### Minor Analysis Improvements
+
+* The query `cs/useless-tostring-call` has been updated to avoid false
+ positive results in calls to `StringBuilder.AppendLine` and calls of
+ the form `base.ToString()`. Moreover, the alert message has been
+ made more precise.
diff --git a/csharp/ql/src/codeql-pack.release.yml b/csharp/ql/src/codeql-pack.release.yml
index f8e54f30a672..7bdec0d85c73 100644
--- a/csharp/ql/src/codeql-pack.release.yml
+++ b/csharp/ql/src/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 1.6.6
+lastReleaseVersion: 1.7.1
diff --git a/csharp/ql/src/qlpack.yml b/csharp/ql/src/qlpack.yml
index 728074e959dc..25b04cf2dc66 100644
--- a/csharp/ql/src/qlpack.yml
+++ b/csharp/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/csharp-queries
-version: 1.6.7-dev
+version: 1.7.2-dev
groups:
- csharp
- queries
diff --git a/csharp/ql/test/query-tests/Linq/MissedSelectOpportunity/MissedSelectOpportunity.cs b/csharp/ql/test/query-tests/Linq/MissedSelectOpportunity/MissedSelectOpportunity.cs
new file mode 100644
index 000000000000..9655a5a0fa9c
--- /dev/null
+++ b/csharp/ql/test/query-tests/Linq/MissedSelectOpportunity/MissedSelectOpportunity.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+class MissedSelectOpportunity
+{
+ public void M1(List lst)
+ {
+ // BAD: Can be replaced with lst.Select(i => i * i)
+ foreach (int i in lst)
+ {
+ int j = i * i;
+ Console.WriteLine(j);
+ } // $ Alert
+ }
+
+ public async Task M2(IEnumerable counters)
+ {
+ // GOOD: Cannot use Select because the initializer contains an await expression
+ foreach (var counter in counters)
+ {
+ var count = await counter.CountAsync();
+ Console.WriteLine(count);
+ }
+ }
+
+ public interface ICounter
+ {
+ Task CountAsync();
+ }
+}
diff --git a/csharp/ql/test/query-tests/Linq/MissedSelectOpportunity/MissedSelectOpportunity.expected b/csharp/ql/test/query-tests/Linq/MissedSelectOpportunity/MissedSelectOpportunity.expected
new file mode 100644
index 000000000000..bc6d464fa3b9
--- /dev/null
+++ b/csharp/ql/test/query-tests/Linq/MissedSelectOpportunity/MissedSelectOpportunity.expected
@@ -0,0 +1 @@
+| MissedSelectOpportunity.cs:11:9:15:9 | foreach (... ... in ...) ... | This foreach loop immediately $@ - consider mapping the sequence explicitly using '.Select(...)'. | MissedSelectOpportunity.cs:13:13:13:26 | ... ...; | maps its iteration variable to another variable |
diff --git a/csharp/ql/test/query-tests/Linq/MissedSelectOpportunity/MissedSelectOpportunity.qlref b/csharp/ql/test/query-tests/Linq/MissedSelectOpportunity/MissedSelectOpportunity.qlref
new file mode 100644
index 000000000000..722d84896800
--- /dev/null
+++ b/csharp/ql/test/query-tests/Linq/MissedSelectOpportunity/MissedSelectOpportunity.qlref
@@ -0,0 +1,2 @@
+query: Linq/MissedSelectOpportunity.ql
+postprocess: utils/test/InlineExpectationsTestQuery.ql
diff --git a/csharp/ql/test/query-tests/Linq/MissedSelectOpportunity/options b/csharp/ql/test/query-tests/Linq/MissedSelectOpportunity/options
new file mode 100644
index 000000000000..75c39b4541ba
--- /dev/null
+++ b/csharp/ql/test/query-tests/Linq/MissedSelectOpportunity/options
@@ -0,0 +1,2 @@
+semmle-extractor-options: /nostdlib /noconfig
+semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj
diff --git a/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.cs b/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.cs
index 981b36002663..01c770d105b4 100644
--- a/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.cs
+++ b/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.cs
@@ -1,16 +1,24 @@
using System;
+using System.Text;
class RedundantToString
{
public void M(object o)
{
- Console.WriteLine(o.ToString()); // BAD
+ Console.WriteLine(o.ToString()); // $ Alert
Console.WriteLine(o); // GOOD
- Console.WriteLine($"Hello: {o.ToString()}"); // BAD
+ Console.WriteLine($"Hello: {o.ToString()}"); // $ Alert
Console.WriteLine($"Hello: {o}"); // GOOD
- Console.WriteLine("Hello: " + o.ToString()); // BAD
+ Console.WriteLine("Hello: " + o.ToString()); // $ Alert
Console.WriteLine("Hello: " + o); // GOOD
+
+ var sb = new StringBuilder();
+ sb.Append(o.ToString()); // $ Alert
+ sb.Append(o); // GOOD
+ sb.AppendLine(o.ToString()); // GOOD
+
+ Console.WriteLine($"Hello: {base.ToString()}"); // GOOD
}
}
diff --git a/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.expected b/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.expected
index 28775378f049..b81421da571f 100644
--- a/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.expected
+++ b/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.expected
@@ -1,4 +1,5 @@
-| RedundantToStringCall.cs:7:27:7:38 | call to method ToString | Redundant call to 'ToString' on a String object. |
-| RedundantToStringCall.cs:10:37:10:48 | call to method ToString | Redundant call to 'ToString' on a String object. |
-| RedundantToStringCall.cs:13:39:13:50 | call to method ToString | Redundant call to 'ToString' on a String object. |
-| RedundantToStringCallBad.cs:7:45:7:56 | call to method ToString | Redundant call to 'ToString' on a String object. |
+| RedundantToStringCall.cs:8:27:8:38 | call to method ToString | Redundant call to 'ToString'. |
+| RedundantToStringCall.cs:11:37:11:48 | call to method ToString | Redundant call to 'ToString'. |
+| RedundantToStringCall.cs:14:39:14:50 | call to method ToString | Redundant call to 'ToString'. |
+| RedundantToStringCall.cs:18:19:18:30 | call to method ToString | Redundant call to 'ToString'. |
+| RedundantToStringCallBad.cs:7:45:7:56 | call to method ToString | Redundant call to 'ToString'. |
diff --git a/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.qlref b/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.qlref
index c0ee8dd0ec7d..86bf1476007d 100644
--- a/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.qlref
+++ b/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCall.qlref
@@ -1 +1,3 @@
-Useless code/RedundantToStringCall.ql
\ No newline at end of file
+query: Useless code/RedundantToStringCall.ql
+postprocess:
+ - utils/test/InlineExpectationsTestQuery.ql
diff --git a/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCallBad.cs b/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCallBad.cs
index d6d043f23762..ed5cd137a855 100644
--- a/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCallBad.cs
+++ b/csharp/ql/test/query-tests/Useless Code/RedundantToStringCall/RedundantToStringCallBad.cs
@@ -4,6 +4,6 @@ class Bad
{
static string Hello(object o)
{
- return string.Format("Hello, {0}!", o.ToString());
+ return string.Format("Hello, {0}!", o.ToString()); // $ Alert
}
}
diff --git a/csharp/ql/test/utils/modelgenerator/dataflow/Summaries.cs b/csharp/ql/test/utils/modelgenerator/dataflow/Summaries.cs
index b59513504d9d..4c85b397ac1f 100644
--- a/csharp/ql/test/utils/modelgenerator/dataflow/Summaries.cs
+++ b/csharp/ql/test/utils/modelgenerator/dataflow/Summaries.cs
@@ -536,6 +536,12 @@ public void Apply(Action