diff --git a/MODULE.bazel b/MODULE.bazel
index d5c2c8784e2..16b4a4691f8 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 03201c9603a..e84ba38d180 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 00000000000..99c04e352df
--- /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 00000000000..23b06db4967
--- /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 3201cd9b063..69fb16e4c39 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 116c231c30a..00000000000
--- 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 129c8beb020..00000000000
--- 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 bc4a8ba134b..6e78fc546b3 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 e42a19a8168..96f8d266206 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 a8bd8a5f93d..00f601fd5da 100644
--- a/actions/ql/src/Security/CWE-275/MissingActionsPermissions.ql
+++ b/actions/ql/src/Security/CWE-275/MissingActionsPermissions.ql
@@ -26,10 +26,23 @@ string permissionsForJob(Job job) {
"{" + concat(string permission | permission = jobNeedsPermission(job) | permission, ", ") + "}"
}
+predicate jobHasPermissions(Job job) {
+ exists(job.getPermissions())
+ or
+ exists(job.getEnclosingWorkflow().getPermissions())
+ or
+ // The workflow is reusable and cannot be triggered in any other way; check callers
+ exists(ReusableWorkflow r | r = job.getEnclosingWorkflow() |
+ not exists(Event e | e = r.getOn().getAnEvent() | e.getName() != "workflow_call") and
+ forall(Job caller | caller = job.getEnclosingWorkflow().(ReusableWorkflow).getACaller() |
+ jobHasPermissions(caller)
+ )
+ )
+}
+
from Job job, string permissions
where
- not exists(job.getPermissions()) and
- not exists(job.getEnclosingWorkflow().getPermissions()) and
+ not jobHasPermissions(job) and
// exists a trigger event that is not a workflow_call
exists(Event e |
e = job.getATriggerEvent() and
diff --git a/actions/ql/src/Security/CWE-829/ArtifactPoisoningCritical.ql b/actions/ql/src/Security/CWE-829/ArtifactPoisoningCritical.ql
index 24ecb4b0339..be49de830c3 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 d2aff7da95f..49dc856e566 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 00000000000..b9d9e69c728
--- /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 00000000000..8bf43e63907
--- /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 f4aa7271ace..e83bac0046e 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 3f76a5273f1..c815afc498c 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 00000000000..717cdabc302
--- /dev/null
+++ b/actions/ql/test/query-tests/Security/CWE-275/.github/workflows/perms11.yml
@@ -0,0 +1,9 @@
+on:
+ workflow_call:
+
+jobs:
+ build:
+ name: Build and test
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/deploy-pages
diff --git a/actions/ql/test/query-tests/Security/CWE-275/.github/workflows/perms12.yml b/actions/ql/test/query-tests/Security/CWE-275/.github/workflows/perms12.yml
new file mode 100644
index 00000000000..25ac1f53248
--- /dev/null
+++ b/actions/ql/test/query-tests/Security/CWE-275/.github/workflows/perms12.yml
@@ -0,0 +1,11 @@
+on:
+ workflow_dispatch:
+
+permissions:
+ contents: read
+ id-token: write
+ pages: write
+
+jobs:
+ call-workflow:
+ uses: ./.github/workflows/perms11.yml
diff --git a/actions/ql/test/query-tests/Security/CWE-829/ArtifactPoisoningCritical.expected b/actions/ql/test/query-tests/Security/CWE-829/ArtifactPoisoningCritical.expected
index 2d29cd9b79b..3c5f6bf93e9 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 57d240fd795..d4b80599950 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 686195e9211..2cd1bcede35 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 07235e047d4..00000000000
--- 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-20-data-extensions-barriers.md b/cpp/ql/lib/change-notes/2026-03-20-data-extensions-barriers.md
new file mode 100644
index 00000000000..30f0092a4e9
--- /dev/null
+++ b/cpp/ql/lib/change-notes/2026-03-20-data-extensions-barriers.md
@@ -0,0 +1,4 @@
+---
+category: feature
+---
+* Data flow barriers and barrier guards can now be added using data extensions. For more information see [Customizing library models for C and C++](https://codeql.github.com/docs/codeql-language-guides/customizing-library-models-for-cpp/).
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 c3bd4028ee9..00000000000
--- 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 c11329a3d9f..00000000000
--- 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 41d77b518f1..00000000000
--- 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 8bf87900330..00000000000
--- 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 54a0ad81036..00000000000
--- 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 c18de40b85d..00000000000
--- 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/released/10.0.0.md b/cpp/ql/lib/change-notes/released/10.0.0.md
new file mode 100644
index 00000000000..af591bd1a0a
--- /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 00000000000..2f97209a02d
--- /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 1be4ac8d0d3..28758256b94 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 16b3d5bceba..227cc4176c0 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 802f3c3e4de..8a9d60a7fa9 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/ConfigurationTestFile.qll b/cpp/ql/lib/semmle/code/cpp/ConfigurationTestFile.qll
index b39c1009f07..ae90caa0e63 100644
--- a/cpp/ql/lib/semmle/code/cpp/ConfigurationTestFile.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ConfigurationTestFile.qll
@@ -42,3 +42,10 @@ class MesonPrivateTestFile extends ConfigurationTestFile {
)
}
}
+
+/**
+ * A file created by a GNU autoconf configure script to test the system configuration.
+ */
+class AutoconfConfigureTestFile extends ConfigurationTestFile {
+ AutoconfConfigureTestFile() { this.getBaseName().regexpMatch("conftest[0-9]*\\.c(pp)?") }
+}
diff --git a/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll b/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll
index d189dd36f87..624465761c2 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 f032ba4749e..98280a522cf 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 8b71f140b01..fb2108c2ac5 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 1a572c221d9..22c74c2aa71 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/ExternalFlowExtensions.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/ExternalFlowExtensions.qll
@@ -33,7 +33,7 @@ extensible predicate barrierModel(
*/
extensible predicate barrierGuardModel(
string namespace, string type, boolean subtypes, string name, string signature, string ext,
- string input, string acceptingvalue, string kind, string provenance, QlBuiltins::ExtensionId madId
+ string input, string acceptingValue, string kind, string provenance, QlBuiltins::ExtensionId madId
);
/**
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll
index cce1b80e7fc..d91dc41febe 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll
@@ -162,13 +162,13 @@ module SourceSinkInterpretationInput implements
}
predicate barrierGuardElement(
- Element e, string input, Public::AcceptingValue acceptingvalue, string kind,
+ Element e, string input, Public::AcceptingValue acceptingValue, string kind,
Public::Provenance provenance, string model
) {
exists(
string package, string type, boolean subtypes, string name, string signature, string ext
|
- barrierGuardModel(package, type, subtypes, name, signature, ext, input, acceptingvalue, kind,
+ barrierGuardModel(package, type, subtypes, name, signature, ext, input, acceptingValue, kind,
provenance, model) and
e = interpretElement(package, type, subtypes, name, signature, ext)
)
diff --git a/cpp/ql/lib/semmle/code/cpp/models/interfaces/NonThrowing.qll b/cpp/ql/lib/semmle/code/cpp/models/interfaces/NonThrowing.qll
index 85b9b66cd66..04826a487ca 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 111b9953395..a781bab07c3 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 126b1d9efa3..80b9ad0e475 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 6747d177c80..b05bd637dc2 100644
--- a/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql
+++ b/cpp/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql
@@ -5,7 +5,7 @@
* @kind problem
* @problem.severity warning
* @security-severity 8.1
- * @precision medium
+ * @precision high
* @id cpp/integer-multiplication-cast-to-long
* @tags reliability
* security
diff --git a/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql b/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql
index 7f0a4833cb5..5842b9474f7 100644
--- a/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql
+++ b/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql
@@ -5,7 +5,7 @@
* @kind problem
* @problem.severity error
* @security-severity 7.5
- * @precision medium
+ * @precision high
* @id cpp/wrong-type-format-argument
* @tags reliability
* correctness
diff --git a/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.qhelp b/cpp/ql/src/Likely Bugs/Underspecified Functions/ImplicitFunctionDeclaration.qhelp
index 6ff60d38341..90a98e1bf57 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 6a55557cf70..00b29efbd0f 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 2dced5d8d84..dbb457db505 100644
--- a/cpp/ql/src/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.qll
+++ b/cpp/ql/src/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.qll
@@ -79,9 +79,7 @@ private predicate hasZeroParamDecl(Function f) {
// True if this file (or header) was compiled as a C file
private predicate isCompiledAsC(File f) {
- f.compiledAsC()
- or
- exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
+ exists(File src | src.compiledAsC() | src.getAnIncludedFile*() = f)
}
predicate mistypedFunctionArguments(FunctionCall fc, Function f, Parameter p) {
diff --git a/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.qll b/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.qll
index 218a54b36c5..fd323513a49 100644
--- a/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.qll
+++ b/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.qll
@@ -28,9 +28,7 @@ private predicate hasZeroParamDecl(Function f) {
/* Holds if this file (or header) was compiled as a C file. */
private predicate isCompiledAsC(File f) {
- f.compiledAsC()
- or
- exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
+ exists(File src | src.compiledAsC() | src.getAnIncludedFile*() = f)
}
/** Holds if `fc` is a call to `f` with too few arguments. */
diff --git a/cpp/ql/src/Likely Bugs/Underspecified Functions/TooManyArguments.qll b/cpp/ql/src/Likely Bugs/Underspecified Functions/TooManyArguments.qll
index 7fba78b5550..ab2a98ae3a5 100644
--- a/cpp/ql/src/Likely Bugs/Underspecified Functions/TooManyArguments.qll
+++ b/cpp/ql/src/Likely Bugs/Underspecified Functions/TooManyArguments.qll
@@ -19,9 +19,7 @@ private predicate hasZeroParamDecl(Function f) {
// True if this file (or header) was compiled as a C file
private predicate isCompiledAsC(File f) {
- f.compiledAsC()
- or
- exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
+ exists(File src | src.compiledAsC() | src.getAnIncludedFile*() = f)
}
predicate tooManyArguments(FunctionCall fc, Function f) {
diff --git a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql
index 3f330807304..7d9ef88adea 100644
--- a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql
+++ b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql
@@ -6,7 +6,7 @@
* @kind problem
* @problem.severity warning
* @security-severity 7.8
- * @precision medium
+ * @precision high
* @tags reliability
* security
* external/cwe/cwe-190
diff --git a/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql b/cpp/ql/src/Security/CWE/CWE-468/SuspiciousAddWithSizeof.ql
index 343e96a00d3..d5a5cd8f665 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 4d4a66c0a22..00000000000
--- 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 0810e9c49ba..00000000000
--- 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 84aef7791fc..00000000000
--- 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 387e2d44b46..00000000000
--- 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 6a1133917bf..00000000000
--- 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 0db0d7c718d..00000000000
--- 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 00000000000..3bbb9480660
--- /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 00000000000..83781b87c58
--- /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 b41e6e78a66..ef7a789e0cf 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 3160da2efb6..714167434c8 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/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/autoconf/ExprHasNoEffect.expected b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/autoconf/ExprHasNoEffect.expected
new file mode 100644
index 00000000000..a87d2ddbd1b
--- /dev/null
+++ b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/autoconf/ExprHasNoEffect.expected
@@ -0,0 +1,2 @@
+| conftest.c.c:4:3:4:8 | call to strlen | This expression has no effect (because $@ has no external side effects). | conftest.h:3:8:3:13 | strlen | strlen |
+| conftest_abc.c:4:3:4:8 | call to strlen | This expression has no effect (because $@ has no external side effects). | conftest.h:3:8:3:13 | strlen | strlen |
diff --git a/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/autoconf/ExprHasNoEffect.qlref b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/autoconf/ExprHasNoEffect.qlref
new file mode 100644
index 00000000000..82a90f5413a
--- /dev/null
+++ b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/autoconf/ExprHasNoEffect.qlref
@@ -0,0 +1 @@
+Likely Bugs/Likely Typos/ExprHasNoEffect.ql
diff --git a/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/autoconf/conftest.c b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/autoconf/conftest.c
new file mode 100644
index 00000000000..2e067f5c433
--- /dev/null
+++ b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/autoconf/conftest.c
@@ -0,0 +1,6 @@
+#include "conftest.h"
+
+int main2() {
+ strlen(""); // GOOD: conftest files are ignored
+ return 0;
+}
diff --git a/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/autoconf/conftest.c.c b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/autoconf/conftest.c.c
new file mode 100644
index 00000000000..4ff7c225335
--- /dev/null
+++ b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/autoconf/conftest.c.c
@@ -0,0 +1,6 @@
+#include "conftest.h"
+
+int main3() {
+ strlen(""); // BAD: not a `conftest` file, as `conftest` is not directly followed by the extension or a sequence of numbers.
+ return 0;
+}
diff --git a/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/autoconf/conftest.cpp b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/autoconf/conftest.cpp
new file mode 100644
index 00000000000..7b8edf64261
--- /dev/null
+++ b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/autoconf/conftest.cpp
@@ -0,0 +1,6 @@
+#include "conftest.h"
+
+int main4() {
+ strlen(""); // GOOD: conftest files are ignored
+ return 0;
+}
diff --git a/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/autoconf/conftest.h b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/autoconf/conftest.h
new file mode 100644
index 00000000000..9cf6f7e0d9f
--- /dev/null
+++ b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/autoconf/conftest.h
@@ -0,0 +1,3 @@
+typedef long long size_t;
+
+size_t strlen(const char *s);
diff --git a/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/autoconf/conftest123.c b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/autoconf/conftest123.c
new file mode 100644
index 00000000000..b227d53ad2a
--- /dev/null
+++ b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/autoconf/conftest123.c
@@ -0,0 +1,6 @@
+#include "conftest.h"
+
+int main5() {
+ strlen(""); // GOOD: conftest files are ignored
+ return 0;
+}
diff --git a/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/autoconf/conftest_abc.c b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/autoconf/conftest_abc.c
new file mode 100644
index 00000000000..88215d7434c
--- /dev/null
+++ b/cpp/ql/test/query-tests/Likely Bugs/Likely Typos/ExprHasNoEffect/autoconf/conftest_abc.c
@@ -0,0 +1,6 @@
+#include "conftest.h"
+
+int main1() {
+ strlen(""); // BAD: not a `conftest` file, as `conftest` is not directly followed by the extension or a sequence of numbers.
+ return 0;
+}
diff --git a/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md b/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md
index dfbc0f9f376..166a94bd88d 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 00000000000..47290bbbeb3
--- /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 00000000000..12bf5dad4b0
--- /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 3d19252da0b..bf581427d29 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 972746e9255..9d0e0ffd4f9 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 dfbc0f9f376..166a94bd88d 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 00000000000..47290bbbeb3
--- /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 00000000000..12bf5dad4b0
--- /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 3d19252da0b..bf581427d29 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 7a4e4fff627..f5203f4e443 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 4546c1d174b..36791fc8c37 100644
--- a/csharp/ql/examples/snippets/integer_literal.ql
+++ b/csharp/ql/examples/snippets/integer_literal.ql
@@ -9,5 +9,5 @@
import csharp
from IntegerLiteral literal
-where literal.getValue().toInt() = 0
+where literal.getIntValue() = 0
select literal
diff --git a/csharp/ql/lib/CHANGELOG.md b/csharp/ql/lib/CHANGELOG.md
index 7261891eed7..32cd8f33c65 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 d490868f453..2a4d5c8c27a 100644
--- a/csharp/ql/lib/Linq/Helpers.qll
+++ b/csharp/ql/lib/Linq/Helpers.qll
@@ -77,7 +77,7 @@ predicate missedAllOpportunity(ForeachStmtGenericEnumerable fes) {
// The then case of the if assigns false to something and breaks out of the loop.
exists(Assignment a, BoolLiteral bl |
a = is.getThen().getAChild*() and
- bl = a.getRValue() and
+ bl = a.getRightOperand() and
bl.toString() = "false"
) and
is.getThen().getAChild*() instanceof BreakStmt
@@ -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-20-data-extensions-barriers.md b/csharp/ql/lib/change-notes/2026-03-20-data-extensions-barriers.md
new file mode 100644
index 00000000000..6408acc7dae
--- /dev/null
+++ b/csharp/ql/lib/change-notes/2026-03-20-data-extensions-barriers.md
@@ -0,0 +1,4 @@
+---
+category: feature
+---
+* Data flow barriers and barrier guards can now be added using data extensions. For more information see [Customizing library models for C#](https://codeql.github.com/docs/codeql-language-guides/customizing-library-models-for-csharp/).
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 159ab1ee3c6..00000000000
--- 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 65ce217b105..506fae5a15e 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/released/5.5.0.md b/csharp/ql/lib/change-notes/released/5.5.0.md
new file mode 100644
index 00000000000..b497d8ea51b
--- /dev/null
+++ b/csharp/ql/lib/change-notes/released/5.5.0.md
@@ -0,0 +1,5 @@
+## 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 f742ee59b53..4b8cf9533c1 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/definitions.qll b/csharp/ql/lib/definitions.qll
index 4feaf20629c..d4f2540bbef 100644
--- a/csharp/ql/lib/definitions.qll
+++ b/csharp/ql/lib/definitions.qll
@@ -96,7 +96,7 @@ private class MethodUse extends Use, QualifiableExpr {
private class AccessUse extends Access, Use {
AccessUse() {
not this.getTarget().(Parameter).getCallable() instanceof Accessor and
- not this = any(LocalVariableDeclAndInitExpr d).getLValue() and
+ not this = any(LocalVariableDeclAndInitExpr d).getLeftOperand() and
not this.isImplicit() and
not this instanceof MethodAccess and // handled by `MethodUse`
not this instanceof TypeAccess and // handled by `TypeMentionUse`
diff --git a/csharp/ql/lib/qlpack.yml b/csharp/ql/lib/qlpack.yml
index b45bd57ad56..7c906e033ad 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/Assignable.qll b/csharp/ql/lib/semmle/code/csharp/Assignable.qll
index a0e575218ad..7075626aa3b 100644
--- a/csharp/ql/lib/semmle/code/csharp/Assignable.qll
+++ b/csharp/ql/lib/semmle/code/csharp/Assignable.qll
@@ -235,7 +235,7 @@ private class RefArg extends AssignableAccess {
module AssignableInternal {
private predicate tupleAssignmentDefinition(AssignExpr ae, Expr leaf) {
exists(TupleExpr te |
- ae.getLValue() = te and
+ ae.getLeftOperand() = te and
te.getAnArgument+() = leaf and
// `leaf` is either an assignable access or a local variable declaration
not leaf instanceof TupleExpr
@@ -249,8 +249,8 @@ module AssignableInternal {
*/
private predicate tupleAssignmentPair(AssignExpr ae, Expr left, Expr right) {
tupleAssignmentDefinition(ae, _) and
- left = ae.getLValue() and
- right = ae.getRValue()
+ left = ae.getLeftOperand() and
+ right = ae.getRightOperand()
or
exists(TupleExpr l, TupleExpr r, int i | tupleAssignmentPair(ae, l, r) |
left = l.getArgument(i) and
@@ -291,7 +291,7 @@ module AssignableInternal {
cached
newtype TAssignableDefinition =
TAssignmentDefinition(Assignment a) {
- not a.getLValue() instanceof TupleExpr and
+ not a.getLeftOperand() instanceof TupleExpr and
not a instanceof AssignCallOperation and
not a instanceof AssignCoalesceExpr
} or
@@ -358,7 +358,7 @@ module AssignableInternal {
// Not defined by dispatch in order to avoid too conservative negative recursion error
cached
AssignableAccess getTargetAccess(AssignableDefinition def) {
- def = TAssignmentDefinition(any(Assignment a | a.getLValue() = result))
+ def = TAssignmentDefinition(any(Assignment a | a.getLeftOperand() = result))
or
def = TTupleAssignmentDefinition(_, result)
or
@@ -381,8 +381,8 @@ module AssignableInternal {
tupleAssignmentPair(ae, ac, result)
)
or
- exists(Assignment ass | ac = ass.getLValue() |
- result = ass.getRValue() and
+ exists(Assignment ass | ac = ass.getLeftOperand() |
+ result = ass.getRightOperand() and
not ass instanceof AssignOperation
)
or
@@ -527,7 +527,7 @@ module AssignableDefinitions {
Assignment getAssignment() { result = a }
override Expr getSource() {
- result = a.getRValue() and
+ result = a.getRightOperand() and
not a instanceof AddOrRemoveEventExpr
}
diff --git a/csharp/ql/lib/semmle/code/csharp/Conversion.qll b/csharp/ql/lib/semmle/code/csharp/Conversion.qll
index ec7ef9cac95..e151944dc38 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/PrintAst.qll b/csharp/ql/lib/semmle/code/csharp/PrintAst.qll
index 1ac96c85e78..1fab6b0f8c4 100644
--- a/csharp/ql/lib/semmle/code/csharp/PrintAst.qll
+++ b/csharp/ql/lib/semmle/code/csharp/PrintAst.qll
@@ -343,10 +343,10 @@ final class AssignmentNode extends ControlFlowElementNode {
result.(TypeMentionNode).getTarget() = controlFlowElement
or
childIndex = 0 and
- result.(ElementNode).getElement() = assignment.getLValue()
+ result.(ElementNode).getElement() = assignment.getLeftOperand()
or
childIndex = 1 and
- result.(ElementNode).getElement() = assignment.getRValue()
+ result.(ElementNode).getElement() = assignment.getRightOperand()
}
}
diff --git a/csharp/ql/lib/semmle/code/csharp/Property.qll b/csharp/ql/lib/semmle/code/csharp/Property.qll
index bbd4fdd9d8e..c9a338d0359 100644
--- a/csharp/ql/lib/semmle/code/csharp/Property.qll
+++ b/csharp/ql/lib/semmle/code/csharp/Property.qll
@@ -535,8 +535,8 @@ class Setter extends Accessor, @setter {
exists(AssignExpr assign |
this.getStatementBody().getNumberOfStmts() = 1 and
assign.getParent() = this.getStatementBody().getAChild() and
- assign.getLValue() = result.getAnAccess() and
- assign.getRValue() = accessToValue()
+ assign.getLeftOperand() = result.getAnAccess() and
+ assign.getRightOperand() = accessToValue()
)
}
diff --git a/csharp/ql/lib/semmle/code/csharp/commons/ComparisonTest.qll b/csharp/ql/lib/semmle/code/csharp/commons/ComparisonTest.qll
index b4641560892..776e2e97c37 100644
--- a/csharp/ql/lib/semmle/code/csharp/commons/ComparisonTest.qll
+++ b/csharp/ql/lib/semmle/code/csharp/commons/ComparisonTest.qll
@@ -161,7 +161,7 @@ private newtype TComparisonTest =
compare.getComparisonKind().isCompare() and
outerKind = outer.getComparisonKind() and
outer.getAnArgument() = compare.getExpr() and
- i = outer.getAnArgument().getValue().toInt()
+ i = outer.getAnArgument().getIntValue()
|
outerKind.isEquality() and
(
diff --git a/csharp/ql/lib/semmle/code/csharp/commons/Constants.qll b/csharp/ql/lib/semmle/code/csharp/commons/Constants.qll
index 5025202eb21..a5f1bc43abe 100644
--- a/csharp/ql/lib/semmle/code/csharp/commons/Constants.qll
+++ b/csharp/ql/lib/semmle/code/csharp/commons/Constants.qll
@@ -32,13 +32,13 @@ private module ConstantComparisonOperation {
private int maxValue(Expr expr) {
if convertedType(expr) instanceof IntegralType and exists(expr.getValue())
- then result = expr.getValue().toInt()
+ then result = expr.getIntValue()
else result = convertedType(expr).maxValue()
}
private int minValue(Expr expr) {
if convertedType(expr) instanceof IntegralType and exists(expr.getValue())
- then result = expr.getValue().toInt()
+ then result = expr.getIntValue()
else result = convertedType(expr).minValue()
}
diff --git a/csharp/ql/lib/semmle/code/csharp/commons/Strings.qll b/csharp/ql/lib/semmle/code/csharp/commons/Strings.qll
index 678a1f92816..3fde913358b 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 03a5aa7e2b7..66b591cfcd2 100644
--- a/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll
+++ b/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll
@@ -60,25 +60,16 @@ private module GuardsInput implements
override boolean asBooleanValue() { boolConst(this, result) }
}
- private predicate intConst(Expr e, int i) {
- e.getValue().toInt() = i and
- (
- e.getType() instanceof Enum
- or
- e.getType() instanceof IntegralType
- )
- }
-
private class IntegerConstant extends ConstantExpr {
- IntegerConstant() { intConst(this, _) }
+ IntegerConstant() { exists(this.getIntValue()) }
- override int asIntegerValue() { intConst(this, result) }
+ override int asIntegerValue() { result = this.getIntValue() }
}
private class EnumConst extends ConstantExpr {
EnumConst() { this.getType() instanceof Enum and this.hasValue() }
- override int asIntegerValue() { result = this.getValue().toInt() }
+ override int asIntegerValue() { result = this.getIntValue() }
}
private class StringConstant extends ConstantExpr instanceof StringLiteral {
@@ -136,7 +127,7 @@ private module GuardsInput implements
IdExpr() { this instanceof AssignExpr or this instanceof CastExpr }
Expr getEqualChildExpr() {
- result = this.(AssignExpr).getRValue()
+ result = this.(AssignExpr).getRightOperand()
or
result = this.(CastExpr).getExpr()
}
@@ -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
)
@@ -836,7 +827,7 @@ module Internal {
/** Holds if expression `e2` is a `null` value whenever `e1` is. */
predicate nullValueImpliedUnary(Expr e1, Expr e2) {
- e1 = e2.(AssignExpr).getRValue()
+ e1 = e2.(AssignExpr).getRightOperand()
or
e1 = e2.(Cast).getExpr()
or
@@ -923,7 +914,7 @@ module Internal {
/** Holds if expression `e2` is a non-`null` value whenever `e1` is. */
predicate nonNullValueImpliedUnary(Expr e1, Expr e2) {
e1 = e2.(CastExpr).getExpr() or
- e1 = e2.(AssignExpr).getRValue() or
+ e1 = e2.(AssignExpr).getRightOperand() or
e1 = e2.(NullCoalescingOperation).getAnOperand()
}
diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll
index e29e92d26eb..dc2c7477a35 100644
--- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll
+++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll
@@ -521,7 +521,7 @@ module Expressions {
// ```
// need special treatment, because the accesses `[0]`, `[1]`, and `[2]`
// have no qualifier.
- this = any(MemberInitializer mi).getLValue()
+ this = any(MemberInitializer mi).getLeftOperand()
) and
not exists(AssignableDefinitions::OutRefDefinition def | def.getTargetAccess() = this)
}
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll
index a82779eaa5d..3d690ee1ac4 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/Nullness.qll
@@ -31,7 +31,7 @@ private Expr maybeNullExpr(Expr reason) {
or
result instanceof AsExpr and reason = result
or
- result.(AssignExpr).getRValue() = maybeNullExpr(reason)
+ result.(AssignExpr).getRightOperand() = maybeNullExpr(reason)
or
result.(CastExpr).getExpr() = maybeNullExpr(reason)
or
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 af104d777b8..ab1e75b3d0f 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 109c27de67b..ae530b2d451 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
@@ -528,7 +528,7 @@ module LocalFlow {
e2 =
any(AssignExpr ae |
ae.getParent() = any(ControlFlowElement cfe | not cfe instanceof ExprStmt) and
- e1 = ae.getRValue()
+ e1 = ae.getRightOperand()
)
or
e1 = e2.(ObjectCreation).getInitializer()
@@ -554,7 +554,7 @@ module LocalFlow {
e2 = we
)
or
- exists(AssignExpr ae | ae.getLValue().(TupleExpr) = e2 and ae.getRValue() = e1)
+ exists(AssignExpr ae | ae.getLeftOperand().(TupleExpr) = e2 and ae.getRightOperand() = e1)
or
exists(ControlFlowElement cfe | cfe = e2.(TupleExpr).(PatternExpr).getPatternMatch() |
cfe.(IsExpr).getExpr() = e1
@@ -795,7 +795,7 @@ private predicate fieldOrPropertyStore(ContentSet c, Expr src, Expr q, boolean p
q = we and
mi = we.getInitializer().getAMemberInitializer() and
f = mi.getInitializedMember() and
- src = mi.getRValue() and
+ src = mi.getRightOperand() and
postUpdate = false
)
or
@@ -804,7 +804,7 @@ private predicate fieldOrPropertyStore(ContentSet c, Expr src, Expr q, boolean p
mi = q.(ObjectInitializer).getAMemberInitializer() and
q.getParent() instanceof ObjectCreation and
f = mi.getInitializedMember() and
- src = mi.getRValue() and
+ src = mi.getRightOperand() and
postUpdate = false
)
or
@@ -879,8 +879,8 @@ private predicate arrayStore(Expr src, Expr a, boolean postUpdate) {
// Member initializer, `new C { Array = { [i] = src } }`
exists(MemberInitializer mi |
mi = a.(ObjectInitializer).getAMemberInitializer() and
- mi.getLValue() instanceof ArrayAccess and
- mi.getRValue() = src and
+ mi.getLeftOperand() instanceof ArrayAccess and
+ mi.getRightOperand() = src and
postUpdate = false
)
}
@@ -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)
}
/**
@@ -2582,7 +2599,7 @@ module PostUpdateNodes {
call.getExpr() = init.(CollectionInitializer).getAnElementInitializer()
or
// E.g. `new Dictionary() { [0] = "a", [1] = "b" }`
- call.getExpr() = init.(ObjectInitializer).getAMemberInitializer().getLValue()
+ call.getExpr() = init.(ObjectInitializer).getAMemberInitializer().getLeftOperand()
)
}
@@ -2795,7 +2812,7 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
preservesValue = true
or
exists(AddEventExpr aee |
- nodeFrom.asExpr() = aee.getRValue() and
+ nodeFrom.asExpr() = aee.getRightOperand() and
nodeTo.asExpr().(EventRead).getTarget() = aee.getTarget() and
preservesValue = false
)
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlow.qll
index 024e9cf119d..f8cec8c4d9f 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 3461f0a5186..cd438ece284 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlowExtensions.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ExternalFlowExtensions.qll
@@ -33,7 +33,7 @@ extensible predicate barrierModel(
*/
extensible predicate barrierGuardModel(
string namespace, string type, boolean subtypes, string name, string signature, string ext,
- string input, string acceptingvalue, string kind, string provenance, QlBuiltins::ExtensionId madId
+ string input, string acceptingValue, string kind, string provenance, QlBuiltins::ExtensionId madId
);
/**
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll
index ec6bbf81fee..a7ab18a6290 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/SsaImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll
index 2809f8187b9..83593a5df36 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll
@@ -337,7 +337,7 @@ private module CallGraph {
pred = succ.(DelegateCreation).getArgument()
or
exists(AddEventExpr ae | succ.(EventAccess).getTarget() = ae.getTarget() |
- pred = ae.getRValue()
+ pred = ae.getRightOperand()
)
}
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll
index e3f5deb9898..bea31ed7f55 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ConstantUtils.qll
@@ -23,7 +23,7 @@ predicate systemArrayLengthAccess(PropertyAccess pa) {
* - a read of the `Length` of an array with `val` lengths.
*/
private predicate constantIntegerExpr(ExprNode e, int val) {
- e.getValue().toInt() = val
+ e.getExpr().getIntValue() = val
or
exists(ExprNode src |
e = getAnExplicitDefinitionRead(src) and
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/RangeUtils.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/RangeUtils.qll
index 46153f18dae..171f2d2f59e 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/RangeUtils.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/RangeUtils.qll
@@ -21,7 +21,7 @@ private module Impl {
/** Holds if SSA definition `def` equals `e + delta`. */
predicate ssaUpdateStep(ExplicitDefinition def, ExprNode e, int delta) {
exists(ControlFlow::Node cfn | cfn = def.getControlFlowNode() |
- e = cfn.(ExprNode::Assignment).getRValue() and
+ e = cfn.(ExprNode::Assignment).getRightOperand() and
delta = 0 and
not cfn instanceof ExprNode::AssignOperation
or
@@ -39,7 +39,7 @@ private module Impl {
/** Holds if `e1 + delta` equals `e2`. */
predicate valueFlowStep(ExprNode e2, ExprNode e1, int delta) {
- e2.(ExprNode::AssignExpr).getRValue() = e1 and delta = 0
+ e2.(ExprNode::AssignExpr).getRightOperand() = e1 and delta = 0
or
e2.(ExprNode::UnaryPlusExpr).getOperand() = e1 and delta = 0
or
@@ -207,13 +207,13 @@ module ExprNode {
override CS::Assignment e;
/** Gets the left operand of this assignment. */
- ExprNode getLValue() {
- result = unique(ExprNode res | hasChild(e, e.getLValue(), this, res) | res)
+ ExprNode getLeftOperand() {
+ result = unique(ExprNode res | hasChild(e, e.getLeftOperand(), this, res) | res)
}
/** Gets the right operand of this assignment. */
- ExprNode getRValue() {
- result = unique(ExprNode res | hasChild(e, e.getRValue(), this, res) | res)
+ ExprNode getRightOperand() {
+ result = unique(ExprNode res | hasChild(e, e.getRightOperand(), this, res) | res)
}
}
@@ -225,6 +225,10 @@ module ExprNode {
/** A compound assignment operation. */
class AssignOperation extends Assignment, BinaryOperation {
override CS::AssignOperation e;
+
+ override ExprNode getLeftOperand() { result = Assignment.super.getLeftOperand() }
+
+ override ExprNode getRightOperand() { result = Assignment.super.getRightOperand() }
}
/** A unary operation. */
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll
index d59d7958765..f64eceda134 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll
@@ -168,7 +168,7 @@ private module Impl {
/** Returned an expression that is assigned to `f`. */
ExprNode getAssignedValueToField(Field f) {
result.getExpr() in [
- f.getAnAssignedValue(), any(AssignOperation a | a.getLValue() = f.getAnAccess())
+ f.getAnAssignedValue(), any(AssignOperation a | a.getLeftOperand() = f.getAnAccess())
]
}
@@ -231,7 +231,7 @@ private module Impl {
/** Returns a sub expression of `e` for expression types where the sign depends on the child. */
ExprNode getASubExprWithSameSign(ExprNode e) {
exists(Expr e_, Expr child | hasChild(e_, child, e, result) |
- child = e_.(AssignExpr).getRValue() or
+ child = e_.(AssignExpr).getRightOperand() or
child = e_.(UnaryPlusExpr).getOperand() or
child = e_.(PostIncrExpr).getOperand() or
child = e_.(PostDecrExpr).getOperand() or
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaUtils.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaUtils.qll
index 89117d90ba7..cf4b44239e9 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaUtils.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaUtils.qll
@@ -55,5 +55,5 @@ ExprNode ssaRead(Definition v, int delta) {
or
v.(ExplicitDefinition).getControlFlowNode().(ExprNode::Assignment) = result and delta = 0
or
- result.(ExprNode::AssignExpr).getRValue() = ssaRead(v, delta)
+ result.(ExprNode::AssignExpr).getRightOperand() = ssaRead(v, delta)
}
diff --git a/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll b/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll
index f7f6c7a50be..770fab65f54 100644
--- a/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll
@@ -1348,7 +1348,7 @@ private module Internal {
any(DynamicMemberAccess dma | this = TDispatchDynamicEventAccess(_, dma, _)).getQualifier()
}
- override Expr getArgument(int i) { i = 0 and result = this.getCall().getRValue() }
+ override Expr getArgument(int i) { i = 0 and result = this.getCall().getRightOperand() }
}
/** A call to a constructor using dynamic types. */
diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Access.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Access.qll
index 84375bc7013..d9fb16f0974 100644
--- a/csharp/ql/lib/semmle/code/csharp/exprs/Access.qll
+++ b/csharp/ql/lib/semmle/code/csharp/exprs/Access.qll
@@ -112,7 +112,7 @@ class BaseAccess extends Access, @base_access_expr {
class MemberAccess extends Access, QualifiableExpr, @member_access_expr {
override predicate hasImplicitThisQualifier() {
QualifiableExpr.super.hasImplicitThisQualifier() and
- not exists(MemberInitializer mi | mi.getLValue() = this)
+ not exists(MemberInitializer mi | mi.getLeftOperand() = this)
}
override Member getQualifiedDeclaration() { result = this.getTarget() }
diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Assignment.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Assignment.qll
index 467149011d2..8c7dd80da24 100644
--- a/csharp/ql/lib/semmle/code/csharp/exprs/Assignment.qll
+++ b/csharp/ql/lib/semmle/code/csharp/exprs/Assignment.qll
@@ -20,14 +20,22 @@ class Assignment extends BinaryOperation, @assign_expr {
expr_parent(_, 1, this)
}
- /** Gets the left operand of this assignment. */
- Expr getLValue() { result = this.getLeftOperand() }
+ /**
+ * DEPRECATED: Use `getLeftOperand` instead.
+ *
+ * Gets the left operand of this assignment.
+ */
+ deprecated Expr getLValue() { result = this.getLeftOperand() }
- /** Gets the right operand of this assignment. */
- Expr getRValue() { result = this.getRightOperand() }
+ /**
+ * DEPRECATED: Use `getRightOperand` instead.
+ *
+ * Gets the right operand of this assignment.
+ */
+ deprecated Expr getRValue() { result = this.getRightOperand() }
/** Gets the variable being assigned to, if any. */
- Variable getTargetVariable() { result.getAnAccess() = this.getLValue() }
+ Variable getTargetVariable() { result.getAnAccess() = this.getLeftOperand() }
override string getOperator() { none() }
}
@@ -40,7 +48,12 @@ class LocalVariableDeclAndInitExpr extends LocalVariableDeclExpr, Assignment {
override LocalVariable getTargetVariable() { result = this.getVariable() }
- override LocalVariableAccess getLValue() { result = Assignment.super.getLValue() }
+ /**
+ * DEPRECATED: Use `getLeftOperand` instead.
+ */
+ deprecated override LocalVariableAccess getLValue() { result = this.getLeftOperand() }
+
+ override LocalVariableAccess getLeftOperand() { result = Assignment.super.getLeftOperand() }
override string toString() { result = LocalVariableDeclExpr.super.toString() + " = ..." }
@@ -223,9 +236,12 @@ deprecated class AssignUnsighedRightShiftExpr = AssignUnsignedRightShiftExpr;
*/
class AddOrRemoveEventExpr extends AssignOperation, @assign_event_expr {
/** Gets the event targeted by this event assignment. */
- Event getTarget() { result = this.getLValue().getTarget() }
+ Event getTarget() { result = this.getLeftOperand().getTarget() }
- override EventAccess getLValue() { result = this.getChild(0) }
+ /**
+ * DEPRECATED: Use `getLeftOperand` instead.
+ */
+ deprecated override EventAccess getLValue() { result = this.getLeftOperand() }
override EventAccess getLeftOperand() { result = this.getChild(0) }
}
diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll
index 0d9e414a5c4..272a8e0caae 100644
--- a/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll
+++ b/csharp/ql/lib/semmle/code/csharp/exprs/Call.qll
@@ -773,7 +773,7 @@ class EventCall extends AccessorCall, EventAccessExpr {
override EventAccessor getTarget() {
exists(Event e, AddOrRemoveEventExpr aoree |
e = this.getEvent() and
- aoree.getLValue() = this
+ aoree.getLeftOperand() = this
|
aoree instanceof AddEventExpr and result = e.getAddEventAccessor()
or
@@ -784,8 +784,8 @@ class EventCall extends AccessorCall, EventAccessExpr {
override Expr getArgument(int i) {
i = 0 and
exists(AddOrRemoveEventExpr aoree |
- aoree.getLValue() = this and
- result = aoree.getRValue()
+ aoree.getLeftOperand() = this and
+ result = aoree.getRightOperand()
)
}
diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Creation.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Creation.qll
index 0e16e0da9c3..19ff9fac53b 100644
--- a/csharp/ql/lib/semmle/code/csharp/exprs/Creation.qll
+++ b/csharp/ql/lib/semmle/code/csharp/exprs/Creation.qll
@@ -95,7 +95,7 @@ class MemberInitializer extends AssignExpr {
MemberInitializer() { this.getParent() instanceof ObjectInitializer }
/** Gets the initialized member. */
- Member getInitializedMember() { result.getAnAccess() = this.getLValue() }
+ Member getInitializedMember() { result.getAnAccess() = this.getLeftOperand() }
override string getAPrimaryQlClass() { result = "MemberInitializer" }
}
diff --git a/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll b/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll
index 66764d06479..a26afb00490 100644
--- a/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll
+++ b/csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll
@@ -57,6 +57,13 @@ class Expr extends ControlFlowElement, @expr {
/** Gets the value of this expression, if any */
string getValue() { expr_value(this, result) }
+ /** Gets the integer value of this expression, if any. */
+ cached
+ int getIntValue() {
+ result = this.getValue().toInt() and
+ (this.getType() instanceof IntegralType or this.getType() instanceof Enum)
+ }
+
/** Holds if this expression has a value. */
final predicate hasValue() { exists(this.getValue()) }
@@ -1099,7 +1106,7 @@ class QualifiableExpr extends Expr, @qualifiable_expr {
}
private Expr getAnAssignOrForeachChild() {
- result = any(AssignExpr e).getLValue()
+ result = any(AssignExpr e).getLeftOperand()
or
result = any(ForeachStmt fs).getVariableDeclTuple()
or
diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/Moq.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/Moq.qll
index 9ab9a026fd2..0beec9a84b2 100644
--- a/csharp/ql/lib/semmle/code/csharp/frameworks/Moq.qll
+++ b/csharp/ql/lib/semmle/code/csharp/frameworks/Moq.qll
@@ -41,6 +41,6 @@ class ReturnedByMockObject extends ObjectCreation {
* Gets a value used to initialize a member of this object creation.
*/
Expr getAMemberInitializationValue() {
- result = this.getInitializer().(ObjectInitializer).getAMemberInitializer().getRValue()
+ result = this.getInitializer().(ObjectInitializer).getAMemberInitializer().getRightOperand()
}
}
diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/Sql.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/Sql.qll
index 6b1eb7b67fb..58d6d68bf0e 100644
--- a/csharp/ql/lib/semmle/code/csharp/frameworks/Sql.qll
+++ b/csharp/ql/lib/semmle/code/csharp/frameworks/Sql.qll
@@ -17,14 +17,14 @@ abstract class SqlExpr extends Expr {
class CommandTextAssignmentSqlExpr extends SqlExpr, AssignExpr {
CommandTextAssignmentSqlExpr() {
exists(Property p, SystemDataIDbCommandInterface i, Property text |
- p = this.getLValue().(PropertyAccess).getTarget() and
+ p = this.getLeftOperand().(PropertyAccess).getTarget() and
text = i.getCommandTextProperty()
|
p.overridesOrImplementsOrEquals(text)
)
}
- override Expr getSql() { result = this.getRValue() }
+ override Expr getSql() { result = this.getRightOperand() }
}
/** A construction of an unknown `IDbCommand` object. */
diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/system/runtime/CompilerServices.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/system/runtime/CompilerServices.qll
index 2c0ba292b9c..aca2cb2cb1c 100644
--- a/csharp/ql/lib/semmle/code/csharp/frameworks/system/runtime/CompilerServices.qll
+++ b/csharp/ql/lib/semmle/code/csharp/frameworks/system/runtime/CompilerServices.qll
@@ -81,7 +81,7 @@ class SystemRuntimeCompilerServicesInlineArrayAttribute extends Attribute {
/**
* Gets the length of the inline array.
*/
- int getLength() { result = this.getConstructorArgument(0).getValue().toInt() }
+ int getLength() { result = this.getConstructorArgument(0).getIntValue() }
}
/** An attribute of type `System.Runtime.CompilerServices.OverloadResolutionPriority`. */
@@ -94,5 +94,5 @@ class SystemRuntimeCompilerServicesOverloadResolutionPriorityAttribute extends A
/**
* Gets the priority number.
*/
- int getPriority() { result = this.getConstructorArgument(0).getValue().toInt() }
+ int getPriority() { result = this.getConstructorArgument(0).getIntValue() }
}
diff --git a/csharp/ql/lib/semmle/code/csharp/security/auth/SecureCookies.qll b/csharp/ql/lib/semmle/code/csharp/security/auth/SecureCookies.qll
index 56b6294949b..e7cb6d8e308 100644
--- a/csharp/ql/lib/semmle/code/csharp/security/auth/SecureCookies.qll
+++ b/csharp/ql/lib/semmle/code/csharp/security/auth/SecureCookies.qll
@@ -100,20 +100,20 @@ Expr getAValueForCookiePolicyProp(string prop) {
Expr getAValueForProp(ObjectCreation create, Assignment a, string prop) {
// values set in object init
exists(MemberInitializer init, Expr src, PropertyAccess pa |
- a.getLValue() = pa and
+ a.getLeftOperand() = pa and
pa.getTarget().hasName(prop) and
init = create.getInitializer().(ObjectInitializer).getAMemberInitializer() and
- init.getLValue() = pa and
- DataFlow::localExprFlow(src, init.getRValue()) and
+ init.getLeftOperand() = pa and
+ DataFlow::localExprFlow(src, init.getRightOperand()) and
result = src
)
or
// values set on var that create is assigned to
exists(Expr src, PropertyAccess pa |
- a.getLValue() = pa and
+ a.getLeftOperand() = pa and
pa.getTarget().hasName(prop) and
DataFlow::localExprFlow(create, pa.getQualifier()) and
- DataFlow::localExprFlow(src, a.getRValue()) and
+ DataFlow::localExprFlow(src, a.getRightOperand()) and
result = src
)
}
@@ -138,15 +138,15 @@ private module OnAppendCookieTrackingConfig impl
exists(PropertyWrite pw, Assignment delegateAssign, Callable c |
pw.getProperty().getName() = "OnAppendCookie" and
pw.getProperty().getDeclaringType() instanceof MicrosoftAspNetCoreBuilderCookiePolicyOptions and
- delegateAssign.getLValue() = pw and
+ delegateAssign.getLeftOperand() = pw and
(
exists(LambdaExpr lambda |
- delegateAssign.getRValue() = lambda and
+ delegateAssign.getRightOperand() = lambda and
lambda = c
)
or
exists(DelegateCreation delegate |
- delegateAssign.getRValue() = delegate and
+ delegateAssign.getRightOperand() = delegate and
delegate.getArgument().(CallableAccess).getTarget() = c
)
) and
@@ -159,9 +159,9 @@ private module OnAppendCookieTrackingConfig impl
exists(PropertyWrite pw, Assignment a |
pw.getProperty().getDeclaringType() instanceof MicrosoftAspNetCoreHttpCookieOptions and
pw.getProperty().getName() = getPropertyName() and
- a.getLValue() = pw and
+ a.getLeftOperand() = pw and
exists(Expr val |
- DataFlow::localExprFlow(val, a.getRValue()) and
+ DataFlow::localExprFlow(val, a.getRightOperand()) and
val.getValue() = "true"
) and
sink.asExpr() = pw.getQualifier()
diff --git a/csharp/ql/lib/semmle/code/csharp/security/dataflow/UnsafeDeserializationQuery.qll b/csharp/ql/lib/semmle/code/csharp/security/dataflow/UnsafeDeserializationQuery.qll
index 5b2bd407a5c..3c8911ef807 100644
--- a/csharp/ql/lib/semmle/code/csharp/security/dataflow/UnsafeDeserializationQuery.qll
+++ b/csharp/ql/lib/semmle/code/csharp/security/dataflow/UnsafeDeserializationQuery.qll
@@ -126,16 +126,16 @@ private module TypeNameTrackingConfig implements DataFlow::ConfigSig {
or
node1.getType() instanceof TypeNameHandlingEnum and
exists(PropertyWrite pw, Property p, Assignment a |
- a.getLValue() = pw and
+ a.getLeftOperand() = pw and
pw.getProperty() = p and
p.getDeclaringType() instanceof JsonSerializerSettingsClass and
p.hasName("TypeNameHandling") and
(
- node1.asExpr() = a.getRValue() and
+ node1.asExpr() = a.getRightOperand() and
node2.asExpr() = pw.getQualifier()
or
exists(ObjectInitializer oi |
- node1.asExpr() = oi.getAMemberInitializer().getRValue() and
+ node1.asExpr() = oi.getAMemberInitializer().getRightOperand() and
node2.asExpr() = oi
)
)
diff --git a/csharp/ql/lib/semmle/code/csharp/security/xml/InsecureXMLQuery.qll b/csharp/ql/lib/semmle/code/csharp/security/xml/InsecureXMLQuery.qll
index 1abeaf797b0..765dc2adf54 100644
--- a/csharp/ql/lib/semmle/code/csharp/security/xml/InsecureXMLQuery.qll
+++ b/csharp/ql/lib/semmle/code/csharp/security/xml/InsecureXMLQuery.qll
@@ -84,15 +84,15 @@ private Expr getAValueForProp(ObjectCreation create, string prop) {
// values set in object init
exists(MemberInitializer init |
init = create.getInitializer().(ObjectInitializer).getAMemberInitializer() and
- init.getLValue().(PropertyAccess).getTarget().hasName(prop) and
- result = init.getRValue()
+ init.getLeftOperand().(PropertyAccess).getTarget().hasName(prop) and
+ result = init.getRightOperand()
)
or
// values set on var that create is assigned to
exists(Assignment propAssign |
- DataFlow::localExprFlow(create, propAssign.getLValue().(PropertyAccess).getQualifier()) and
- propAssign.getLValue().(PropertyAccess).getTarget().hasName(prop) and
- result = propAssign.getRValue()
+ DataFlow::localExprFlow(create, propAssign.getLeftOperand().(PropertyAccess).getQualifier()) and
+ propAssign.getLeftOperand().(PropertyAccess).getTarget().hasName(prop) and
+ result = propAssign.getRightOperand()
)
}
diff --git a/csharp/ql/src/CHANGELOG.md b/csharp/ql/src/CHANGELOG.md
index 39bfe944d5d..cdab7134185 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/Dead Code/NonAssignedFields.ql b/csharp/ql/src/Dead Code/NonAssignedFields.ql
index 83aa889b77c..b9e86809749 100644
--- a/csharp/ql/src/Dead Code/NonAssignedFields.ql
+++ b/csharp/ql/src/Dead Code/NonAssignedFields.ql
@@ -84,9 +84,9 @@ where
not f.getDeclaringType() instanceof Enum and
not f.getType() instanceof Struct and
not exists(Assignment ae, Field g |
- ae.getLValue().(FieldAccess).getTarget() = g and
+ ae.getLeftOperand().(FieldAccess).getTarget() = g and
g.getUnboundDeclaration() = f and
- not ae.getRValue() instanceof NullLiteral
+ not ae.getRightOperand() instanceof NullLiteral
) and
not exists(MethodCall mc, int i, Field g |
exists(Parameter p | mc.getTarget().getParameter(i) = p | p.isOut() or p.isRef()) and
@@ -101,7 +101,7 @@ where
not init instanceof NullLiteral
) and
not exists(AssignOperation ua, Field g |
- ua.getLValue().(FieldAccess).getTarget() = g and
+ ua.getLeftOperand().(FieldAccess).getTarget() = g and
g.getUnboundDeclaration() = f
) and
not exists(MutatorOperation op |
diff --git a/csharp/ql/src/Language Abuse/ForeachCapture.ql b/csharp/ql/src/Language Abuse/ForeachCapture.ql
index 03f1f99a044..2ed24b42eba 100644
--- a/csharp/ql/src/Language Abuse/ForeachCapture.ql
+++ b/csharp/ql/src/Language Abuse/ForeachCapture.ql
@@ -60,16 +60,16 @@ module LambdaDataFlow {
}
Element getAssignmentTarget(Expr e) {
- exists(Assignment a | a.getRValue() = e |
- result = a.getLValue().(PropertyAccess).getTarget() or
- result = a.getLValue().(FieldAccess).getTarget() or
- result = a.getLValue().(LocalVariableAccess).getTarget() or
- result = a.getLValue().(EventAccess).getTarget()
+ exists(Assignment a | a.getRightOperand() = e |
+ result = a.getLeftOperand().(PropertyAccess).getTarget() or
+ result = a.getLeftOperand().(FieldAccess).getTarget() or
+ result = a.getLeftOperand().(LocalVariableAccess).getTarget() or
+ result = a.getLeftOperand().(EventAccess).getTarget()
)
or
exists(AddEventExpr aee |
- e = aee.getRValue() and
- result = aee.getLValue().getTarget()
+ e = aee.getRightOperand() and
+ result = aee.getLeftOperand().getTarget()
)
or
result = getCollectionAssignmentTarget(e)
@@ -97,8 +97,8 @@ Element getCollectionAssignmentTarget(Expr e) {
// Store values using indexer
exists(IndexerAccess ia, AssignExpr ae |
ia.getQualifier() = result.(Variable).getAnAccess() and
- ia = ae.getLValue() and
- e = ae.getRValue()
+ ia = ae.getLeftOperand() and
+ e = ae.getRightOperand()
)
}
diff --git a/csharp/ql/src/Language Abuse/MissedTernaryOpportunity.ql b/csharp/ql/src/Language Abuse/MissedTernaryOpportunity.ql
index bd7492b8583..01d6baa9573 100644
--- a/csharp/ql/src/Language Abuse/MissedTernaryOpportunity.ql
+++ b/csharp/ql/src/Language Abuse/MissedTernaryOpportunity.ql
@@ -15,7 +15,7 @@ import csharp
import semmle.code.csharp.commons.StructuralComparison
private Expr getAssignedExpr(Stmt stmt) {
- result = stmt.stripSingletonBlocks().(ExprStmt).getExpr().(AssignExpr).getLValue()
+ result = stmt.stripSingletonBlocks().(ExprStmt).getExpr().(AssignExpr).getLeftOperand()
}
from IfStmt is, string what
diff --git a/csharp/ql/src/Likely Bugs/BadCheckOdd.ql b/csharp/ql/src/Likely Bugs/BadCheckOdd.ql
index 34ae4b632ae..72924f9103d 100644
--- a/csharp/ql/src/Likely Bugs/BadCheckOdd.ql
+++ b/csharp/ql/src/Likely Bugs/BadCheckOdd.ql
@@ -13,7 +13,7 @@
import csharp
predicate isDefinitelyPositive(Expr e) {
- e.getValue().toInt() >= 0 or
+ e.getIntValue() >= 0 or
e.(PropertyAccess).getTarget().hasName("Length") or
e.(MethodCall).getTarget().hasUndecoratedName("Count")
}
@@ -23,12 +23,12 @@ where
t.getLeftOperand() = lhs and
t.getRightOperand() = rhs and
not isDefinitelyPositive(lhs.getLeftOperand().stripCasts()) and
- lhs.getRightOperand().(IntegerLiteral).getValue() = "2" and
+ lhs.getRightOperand().(IntegerLiteral).getIntValue() = 2 and
(
- t instanceof EQExpr and rhs.getValue() = "1" and parity = "oddness"
+ t instanceof EQExpr and rhs.getIntValue() = 1 and parity = "oddness"
or
- t instanceof NEExpr and rhs.getValue() = "1" and parity = "evenness"
+ t instanceof NEExpr and rhs.getIntValue() = 1 and parity = "evenness"
or
- t instanceof GTExpr and rhs.getValue() = "0" and parity = "oddness"
+ t instanceof GTExpr and rhs.getIntValue() = 0 and parity = "oddness"
)
select t, "Possibly invalid test for " + parity + ". This will fail for negative numbers."
diff --git a/csharp/ql/src/Likely Bugs/Collections/WriteOnlyContainer.ql b/csharp/ql/src/Likely Bugs/Collections/WriteOnlyContainer.ql
index 5fcb140e679..046099213cc 100644
--- a/csharp/ql/src/Likely Bugs/Collections/WriteOnlyContainer.ql
+++ b/csharp/ql/src/Likely Bugs/Collections/WriteOnlyContainer.ql
@@ -23,9 +23,10 @@ where
) and
forex(Access a | a = v.getAnAccess() |
a = any(ModifierMethodCall m).getQualifier() or
- a = any(AssignExpr ass | ass.getRValue() instanceof ObjectCreation).getLValue() or
+ a = any(AssignExpr ass | ass.getRightOperand() instanceof ObjectCreation).getLeftOperand() or
a =
- any(LocalVariableDeclAndInitExpr ass | ass.getRValue() instanceof ObjectCreation).getLValue()
+ any(LocalVariableDeclAndInitExpr ass | ass.getRightOperand() instanceof ObjectCreation)
+ .getLeftOperand()
) and
not v = any(ForeachStmt fs).getVariable() and
not v = any(BindingPatternExpr vpe).getVariableDeclExpr().getVariable() and
diff --git a/csharp/ql/src/Likely Bugs/MishandlingJapaneseEra.ql b/csharp/ql/src/Likely Bugs/MishandlingJapaneseEra.ql
index c8df36bf7bf..3e54a3a00db 100644
--- a/csharp/ql/src/Likely Bugs/MishandlingJapaneseEra.ql
+++ b/csharp/ql/src/Likely Bugs/MishandlingJapaneseEra.ql
@@ -27,8 +27,8 @@ predicate isExactEraStartDateCreation(ObjectCreation cr) {
cr.getType().hasFullyQualifiedName("System", "DateTime") or
cr.getType().hasFullyQualifiedName("System", "DateTimeOffset")
) and
- isEraStart(cr.getArgument(0).getValue().toInt(), cr.getArgument(1).getValue().toInt(),
- cr.getArgument(2).getValue().toInt())
+ isEraStart(cr.getArgument(0).getIntValue(), cr.getArgument(1).getIntValue(),
+ cr.getArgument(2).getIntValue())
}
predicate isDateFromJapaneseCalendarToDateTime(MethodCall mc) {
@@ -44,7 +44,7 @@ predicate isDateFromJapaneseCalendarToDateTime(MethodCall mc) {
mc.getNumberOfArguments() = 7 // implicitly current era
or
mc.getNumberOfArguments() = 8 and
- mc.getArgument(7).getValue() = "0"
+ mc.getArgument(7).getIntValue() = 0
) // explicitly current era
}
diff --git a/csharp/ql/src/Likely Bugs/PossibleLossOfPrecision.ql b/csharp/ql/src/Likely Bugs/PossibleLossOfPrecision.ql
index 8b719bb92a5..1c41d24fb3c 100644
--- a/csharp/ql/src/Likely Bugs/PossibleLossOfPrecision.ql
+++ b/csharp/ql/src/Likely Bugs/PossibleLossOfPrecision.ql
@@ -40,8 +40,8 @@ predicate convertedToFloatOrDecimal(Expr e, Type t) {
/** Holds if `div` is an exact integer division. */
predicate exactDivision(DivExpr div) {
exists(int numerator, int denominator |
- numerator = div.getNumerator().stripCasts().getValue().toInt() and
- denominator = div.getDenominator().stripCasts().getValue().toInt() and
+ numerator = div.getNumerator().stripCasts().getIntValue() and
+ denominator = div.getDenominator().stripCasts().getIntValue() and
numerator % denominator = 0
)
}
diff --git a/csharp/ql/src/Likely Bugs/SelfAssignment.ql b/csharp/ql/src/Likely Bugs/SelfAssignment.ql
index f01a1378242..6e51b87a779 100644
--- a/csharp/ql/src/Likely Bugs/SelfAssignment.ql
+++ b/csharp/ql/src/Likely Bugs/SelfAssignment.ql
@@ -19,7 +19,7 @@ private predicate candidate(AssignExpr ae) {
not ae instanceof MemberInitializer and
// Enum field initializers are never self assignments. `enum E { A = 42 }`
not ae.getParent().(Field).getDeclaringType() instanceof Enum and
- forall(Expr e | e = ae.getLValue().getAChildExpr*() |
+ forall(Expr e | e = ae.getLeftOperand().getAChildExpr*() |
// Non-trivial property accesses may have side-effects,
// so these are not considered
e instanceof PropertyAccess implies e instanceof TrivialPropertyAccess
@@ -28,7 +28,7 @@ private predicate candidate(AssignExpr ae) {
private predicate selfAssignExpr(AssignExpr ae) {
candidate(ae) and
- sameGvn(ae.getLValue(), ae.getRValue())
+ sameGvn(ae.getLeftOperand(), ae.getRightOperand())
}
private Declaration getDeclaration(Expr e) {
@@ -40,5 +40,5 @@ private Declaration getDeclaration(Expr e) {
}
from AssignExpr ae, Declaration target
-where selfAssignExpr(ae) and target = getDeclaration(ae.getLValue())
+where selfAssignExpr(ae) and target = getDeclaration(ae.getLeftOperand())
select ae, "This assignment assigns $@ to itself.", target, target.getName()
diff --git a/csharp/ql/src/Linq/BadMultipleIteration.ql b/csharp/ql/src/Linq/BadMultipleIteration.ql
index 8146bbf167d..0f9e335e225 100644
--- a/csharp/ql/src/Linq/BadMultipleIteration.ql
+++ b/csharp/ql/src/Linq/BadMultipleIteration.ql
@@ -50,7 +50,7 @@ predicate potentiallyConsumingAccess(VariableAccess va) {
Expr sequenceSource(IEnumerableSequence seq) {
result = seq.getInitializer()
or
- exists(Assignment a | a.getLValue() = seq.getAnAccess() and result = a.getRValue())
+ exists(Assignment a | a.getLeftOperand() = seq.getAnAccess() and result = a.getRightOperand())
}
from IEnumerableSequence seq, VariableAccess va
diff --git a/csharp/ql/src/Performance/StringConcatenationInLoop.ql b/csharp/ql/src/Performance/StringConcatenationInLoop.ql
index 3b3228588a4..d27b99e7bdd 100644
--- a/csharp/ql/src/Performance/StringConcatenationInLoop.ql
+++ b/csharp/ql/src/Performance/StringConcatenationInLoop.ql
@@ -24,7 +24,7 @@ class StringCat extends AddExpr {
*/
predicate isSelfConcatAssignExpr(AssignExpr e, Variable v) {
exists(VariableAccess use |
- stringCatContains(e.getRValue(), use) and
+ stringCatContains(e.getRightOperand(), use) and
use.getTarget() = e.getTargetVariable() and
v = use.getTarget()
)
@@ -41,7 +41,7 @@ predicate stringCatContains(StringCat expr, Expr child) {
* where `v` is a simple variable (and not, for example, a property).
*/
predicate isConcatExpr(AssignAddExpr e, Variable v) {
- e.getLValue().getType() instanceof StringType and
+ e.getLeftOperand().getType() instanceof StringType and
v = e.getTargetVariable()
}
diff --git a/csharp/ql/src/Security Features/CWE-1004/CookieWithoutHttpOnly.ql b/csharp/ql/src/Security Features/CWE-1004/CookieWithoutHttpOnly.ql
index dcc520540bb..f72de01b5db 100644
--- a/csharp/ql/src/Security Features/CWE-1004/CookieWithoutHttpOnly.ql
+++ b/csharp/ql/src/Security Features/CWE-1004/CookieWithoutHttpOnly.ql
@@ -27,8 +27,8 @@ predicate cookieAppendHttpOnlyByDefault() {
predicate httpOnlyFalse(ObjectCreation oc) {
exists(Assignment a |
- getAValueForProp(oc, a, "HttpOnly") = a.getRValue() and
- a.getRValue().getValue() = "false"
+ getAValueForProp(oc, a, "HttpOnly") = a.getRightOperand() and
+ a.getRightOperand().getValue() = "false"
)
}
@@ -100,8 +100,8 @@ predicate nonHttpOnlyCookieBuilderAssignment(Assignment a, Expr val) {
MicrosoftAspNetCoreAuthenticationCookiesCookieAuthenticationOptions
) and
pw.getProperty().getName() = "HttpOnly" and
- a.getLValue() = pw and
- DataFlow::localExprFlow(val, a.getRValue())
+ a.getLeftOperand() = pw and
+ DataFlow::localExprFlow(val, a.getRightOperand())
)
}
@@ -111,7 +111,7 @@ where
nonHttpOnlyCookieCall(httpOnlySink)
or
exists(Assignment a |
- httpOnlySink = a.getRValue() and
+ httpOnlySink = a.getRightOperand() and
nonHttpOnlyCookieBuilderAssignment(a, _)
)
)
diff --git a/csharp/ql/src/Security Features/CWE-327/InsecureSQLConnection.ql b/csharp/ql/src/Security Features/CWE-327/InsecureSQLConnection.ql
index 330ad1c1c32..a1dd249faba 100644
--- a/csharp/ql/src/Security Features/CWE-327/InsecureSQLConnection.ql
+++ b/csharp/ql/src/Security Features/CWE-327/InsecureSQLConnection.ql
@@ -35,8 +35,8 @@ module InsecureSqlConnectionConfig implements DataFlow::ConfigSig {
) and
not exists(MemberInitializer mi |
mi = oc.getInitializer().(ObjectInitializer).getAMemberInitializer() and
- mi.getLValue().(PropertyAccess).getTarget().getName() = "Encrypt" and
- mi.getRValue().(BoolLiteral).getValue() = "true"
+ mi.getLeftOperand().(PropertyAccess).getTarget().getName() = "Encrypt" and
+ mi.getRightOperand().(BoolLiteral).getValue() = "true"
)
)
}
diff --git a/csharp/ql/src/Security Features/CWE-614/CookieWithoutSecure.ql b/csharp/ql/src/Security Features/CWE-614/CookieWithoutSecure.ql
index ce1f75d627c..1149b4bcad2 100644
--- a/csharp/ql/src/Security Features/CWE-614/CookieWithoutSecure.ql
+++ b/csharp/ql/src/Security Features/CWE-614/CookieWithoutSecure.ql
@@ -31,8 +31,8 @@ predicate cookieAppendSecureByDefault() {
predicate secureFalse(ObjectCreation oc) {
exists(Assignment a |
- getAValueForProp(oc, a, "Secure") = a.getRValue() and
- a.getRValue().getValue() = "false"
+ getAValueForProp(oc, a, "Secure") = a.getRightOperand() and
+ a.getRightOperand().getValue() = "false"
)
}
@@ -96,8 +96,8 @@ predicate insecureSecurePolicyAssignment(Assignment a, Expr val) {
MicrosoftAspNetCoreAuthenticationCookiesCookieAuthenticationOptions
) and
pw.getProperty().getName() = "SecurePolicy" and
- a.getLValue() = pw and
- DataFlow::localExprFlow(val, a.getRValue()) and
+ a.getLeftOperand() = pw and
+ DataFlow::localExprFlow(val, a.getRightOperand()) and
val.getValue() = "2" // None
)
}
@@ -107,7 +107,7 @@ where
insecureCookieCall(secureSink)
or
exists(Assignment a |
- secureSink = a.getRValue() and
+ secureSink = a.getRightOperand() and
insecureSecurePolicyAssignment(a, _)
)
select secureSink, "Cookie attribute 'Secure' is not set to true."
diff --git a/csharp/ql/src/Security Features/CookieWithOverlyBroadDomain.ql b/csharp/ql/src/Security Features/CookieWithOverlyBroadDomain.ql
index 9c6e6935186..59a6340104a 100644
--- a/csharp/ql/src/Security Features/CookieWithOverlyBroadDomain.ql
+++ b/csharp/ql/src/Security Features/CookieWithOverlyBroadDomain.ql
@@ -14,11 +14,11 @@ import csharp
from Assignment a, PropertyAccess pa
where
- a.getLValue() = pa and
+ a.getLeftOperand() = pa and
pa.getTarget().hasName("Domain") and
pa.getTarget().getDeclaringType().hasFullyQualifiedName("System.Web", "HttpCookie") and
(
- a.getRValue().getValue().regexpReplaceAll("[^.]", "").length() < 2 or
- a.getRValue().getValue().matches(".%")
+ a.getRightOperand().getValue().regexpReplaceAll("[^.]", "").length() < 2 or
+ a.getRightOperand().getValue().matches(".%")
)
select a, "Overly broad domain for cookie."
diff --git a/csharp/ql/src/Security Features/CookieWithOverlyBroadPath.ql b/csharp/ql/src/Security Features/CookieWithOverlyBroadPath.ql
index 6690cac47d2..d659f7c8dc5 100644
--- a/csharp/ql/src/Security Features/CookieWithOverlyBroadPath.ql
+++ b/csharp/ql/src/Security Features/CookieWithOverlyBroadPath.ql
@@ -14,8 +14,8 @@ import csharp
from Assignment a, PropertyAccess pa
where
- a.getLValue() = pa and
+ a.getLeftOperand() = pa and
pa.getTarget().hasName("Path") and
pa.getTarget().getDeclaringType().hasFullyQualifiedName("System.Web", "HttpCookie") and
- a.getRValue().getValue() = "/"
+ a.getRightOperand().getValue() = "/"
select a, "Overly broad path for cookie."
diff --git a/csharp/ql/src/Security Features/HeaderCheckingDisabled.ql b/csharp/ql/src/Security Features/HeaderCheckingDisabled.ql
index 7a3a5fdc4f2..bc9bf289c2d 100644
--- a/csharp/ql/src/Security Features/HeaderCheckingDisabled.ql
+++ b/csharp/ql/src/Security Features/HeaderCheckingDisabled.ql
@@ -17,12 +17,12 @@ from Element l
where
// header checking is disabled programmatically in the code
exists(Assignment a, PropertyAccess pa |
- a.getLValue() = pa and
+ a.getLeftOperand() = pa and
pa.getTarget().hasName("EnableHeaderChecking") and
pa.getTarget()
.getDeclaringType()
.hasFullyQualifiedName("System.Web.Configuration", "HttpRuntimeSection") and
- a.getRValue().getValue() = "false" and
+ a.getRightOperand().getValue() = "false" and
a = l
)
or
diff --git a/csharp/ql/src/Security Features/InsecureRandomness.ql b/csharp/ql/src/Security Features/InsecureRandomness.ql
index 8237afdff13..649969a2778 100644
--- a/csharp/ql/src/Security Features/InsecureRandomness.ql
+++ b/csharp/ql/src/Security Features/InsecureRandomness.ql
@@ -89,10 +89,10 @@ module Random {
e = any(SensitiveLibraryParameter v).getAnAssignedArgument()
or
// Assignment operation, e.g. += or similar
- exists(AssignOperation ao | ao.getRValue() = e |
- ao.getLValue() = any(SensitiveVariable v).getAnAccess() or
- ao.getLValue() = any(SensitiveProperty v).getAnAccess() or
- ao.getLValue() = any(SensitiveLibraryParameter v).getAnAccess()
+ exists(AssignOperation ao | ao.getRightOperand() = e |
+ ao.getLeftOperand() = any(SensitiveVariable v).getAnAccess() or
+ ao.getLeftOperand() = any(SensitiveProperty v).getAnAccess() or
+ ao.getLeftOperand() = any(SensitiveLibraryParameter v).getAnAccess()
)
)
}
diff --git a/csharp/ql/src/Security Features/InsufficientKeySize.ql b/csharp/ql/src/Security Features/InsufficientKeySize.ql
index 94ae6b9286f..98a7852fbaf 100644
--- a/csharp/ql/src/Security Features/InsufficientKeySize.ql
+++ b/csharp/ql/src/Security Features/InsufficientKeySize.ql
@@ -20,7 +20,7 @@ predicate incorrectUseOfRC2(Assignment e, string msg) {
.getDeclaringType()
.hasFullyQualifiedName("System.Security.Cryptography", "RC2CryptoServiceProvider")
) and
- e.getRValue().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/Security Features/PersistentCookie.ql b/csharp/ql/src/Security Features/PersistentCookie.ql
index 7f9861213fc..edc97b464e5 100644
--- a/csharp/ql/src/Security Features/PersistentCookie.ql
+++ b/csharp/ql/src/Security Features/PersistentCookie.ql
@@ -52,8 +52,8 @@ class FutureDateExpr extends MethodCall {
from Assignment a, PropertyAccess pa, FutureDateExpr fde
where
- a.getLValue() = pa and
- a.getRValue() = fde and
+ a.getLeftOperand() = pa and
+ a.getRightOperand() = fde and
pa.getTarget().hasName("Expires") and
pa.getTarget().getDeclaringType().hasFullyQualifiedName("System.Web", "HttpCookie") and
(fde.timeIsNotClear() or fde.getTimeInSecond() > 300) // 5 minutes max
diff --git a/csharp/ql/src/Telemetry/DatabaseQuality.qll b/csharp/ql/src/Telemetry/DatabaseQuality.qll
index ca2ab3e7e16..ad7ac682bf5 100644
--- a/csharp/ql/src/Telemetry/DatabaseQuality.qll
+++ b/csharp/ql/src/Telemetry/DatabaseQuality.qll
@@ -27,7 +27,7 @@ module CallTargetStats implements StatsSig {
p = c.getProperty() and
not p.getAnAccessor() instanceof Setter and
assign = c.getParent() and
- assign.getLValue() = c and
+ assign.getLeftOperand() = c and
assign.getParent() instanceof Property
)
}
@@ -36,7 +36,7 @@ module CallTargetStats implements StatsSig {
exists(Property p, AssignExpr assign |
p = c.getProperty() and
assign = c.getParent() and
- assign.getLValue() = c and
+ assign.getLeftOperand() = c and
assign.getParent() instanceof ObjectInitializer and
assign.getParent().getParent() instanceof AnonymousObjectCreation
)
@@ -46,8 +46,8 @@ module CallTargetStats implements StatsSig {
exists(Property p, AssignExpr assign |
p = c.getProperty() and
assign = c.getParent() and
- assign.getLValue() = c and
- assign.getRValue() instanceof ObjectOrCollectionInitializer
+ assign.getLeftOperand() = c and
+ assign.getRightOperand() instanceof ObjectOrCollectionInitializer
)
}
diff --git a/csharp/ql/src/Useless code/RedundantToStringCall.ql b/csharp/ql/src/Useless code/RedundantToStringCall.ql
index 55e7056e9a0..e29e071b4d9 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 c317194bc25..00000000000
--- 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 a1051d4c00f..906a13d68d0 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 00000000000..0b5df9629c6
--- /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 f8e54f30a67..7bdec0d85c7 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/experimental/Security Features/CWE-759/HashWithoutSalt.ql b/csharp/ql/src/experimental/Security Features/CWE-759/HashWithoutSalt.ql
index d43050c2deb..0dc8fc362d6 100644
--- a/csharp/ql/src/experimental/Security Features/CWE-759/HashWithoutSalt.ql
+++ b/csharp/ql/src/experimental/Security Features/CWE-759/HashWithoutSalt.ql
@@ -187,10 +187,10 @@ module HashWithoutSaltConfig implements DataFlow::ConfigSig {
or
// a salt or key is included in subclasses of `KeyedHashAlgorithm`
exists(MethodCall mc, Assignment a, ObjectCreation oc |
- a.getRValue() = oc and
+ a.getRightOperand() = oc and
oc.getObjectType().getABaseType+() instanceof KeyedHashAlgorithm and
mc.getTarget() instanceof HashMethod and
- a.getLValue() = mc.getQualifier().(VariableAccess).getTarget().getAnAccess() and
+ a.getLeftOperand() = mc.getQualifier().(VariableAccess).getTarget().getAnAccess() and
mc.getArgument(0) = node.asExpr()
)
}
diff --git a/csharp/ql/src/qlpack.yml b/csharp/ql/src/qlpack.yml
index 728074e959d..25b04cf2dc6 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/library-tests/assignments/AssignOperation.ql b/csharp/ql/test/library-tests/assignments/AssignOperation.ql
index 2ca3b11a831..2fa23ed0a9f 100644
--- a/csharp/ql/test/library-tests/assignments/AssignOperation.ql
+++ b/csharp/ql/test/library-tests/assignments/AssignOperation.ql
@@ -1,4 +1,4 @@
import csharp
from AssignOperation ao
-select ao, ao.getLValue(), ao.getRValue()
+select ao, ao.getLeftOperand(), ao.getRightOperand()
diff --git a/csharp/ql/test/library-tests/conversion/pointer/Pointer.ql b/csharp/ql/test/library-tests/conversion/pointer/Pointer.ql
index 69e7db8c1cf..450ed9940a8 100644
--- a/csharp/ql/test/library-tests/conversion/pointer/Pointer.ql
+++ b/csharp/ql/test/library-tests/conversion/pointer/Pointer.ql
@@ -1,5 +1,5 @@
import csharp
from Assignment a
-select a.getLocation(), a.getLValue().getType().toString(), a.getRValue().getType().toString(),
- a.getRValue().toString()
+select a.getLocation(), a.getLeftOperand().getType().toString(),
+ a.getRightOperand().getType().toString(), a.getRightOperand().toString()
diff --git a/csharp/ql/test/library-tests/csharp10/lambda.ql b/csharp/ql/test/library-tests/csharp10/lambda.ql
index 3cfec302b52..55c7faac049 100644
--- a/csharp/ql/test/library-tests/csharp10/lambda.ql
+++ b/csharp/ql/test/library-tests/csharp10/lambda.ql
@@ -3,7 +3,7 @@ import csharp
private predicate getLambda(
LocalVariableDeclAndInitExpr e, string type, LocalVariable v, LambdaExpr lexp
) {
- lexp = e.getRValue() and
+ lexp = e.getRightOperand() and
v = e.getTargetVariable() and
type = e.getType().toStringWithTypes()
}
diff --git a/csharp/ql/test/library-tests/csharp11/operators.ql b/csharp/ql/test/library-tests/csharp11/operators.ql
index 607efac0c26..f1543e2d744 100644
--- a/csharp/ql/test/library-tests/csharp11/operators.ql
+++ b/csharp/ql/test/library-tests/csharp11/operators.ql
@@ -14,8 +14,8 @@ query predicate assignbitwise(
AssignBitwiseOperation op, Expr left, Expr right, string name, string qlclass
) {
op.getFile().getStem() = "Operators" and
- left = op.getLValue() and
- right = op.getRValue() and
+ left = op.getLeftOperand() and
+ right = op.getRightOperand() and
name = op.getOperator() and
qlclass = op.getAPrimaryQlClass()
}
diff --git a/csharp/ql/test/library-tests/csharp6/MemberInitializer.ql b/csharp/ql/test/library-tests/csharp6/MemberInitializer.ql
index f3ef63fe225..1895792f07c 100644
--- a/csharp/ql/test/library-tests/csharp6/MemberInitializer.ql
+++ b/csharp/ql/test/library-tests/csharp6/MemberInitializer.ql
@@ -12,7 +12,7 @@ query predicate indexerCalls(IndexerCall indexer, int arg, Expr value) {
query predicate elementAssignments(
ElementWrite write, Assignment assignment, int index, Expr indexer
) {
- write = assignment.getLValue() and indexer = write.getIndex(index)
+ write = assignment.getLeftOperand() and indexer = write.getIndex(index)
}
query predicate arrayQualifiers(ElementAccess access, Expr qualifier) {
diff --git a/csharp/ql/test/library-tests/enums/Enums11.ql b/csharp/ql/test/library-tests/enums/Enums11.ql
index 36b2c005a21..f6133517f7d 100644
--- a/csharp/ql/test/library-tests/enums/Enums11.ql
+++ b/csharp/ql/test/library-tests/enums/Enums11.ql
@@ -6,7 +6,7 @@ import csharp
from Expr e
where
- exists(Assignment a | a.getRValue() = e |
+ exists(Assignment a | a.getRightOperand() = e |
a.getParent().(Field).getDeclaringType() instanceof Enum
)
select e, e.getValue()
diff --git a/csharp/ql/test/library-tests/expressions/AddEventExpr1.ql b/csharp/ql/test/library-tests/expressions/AddEventExpr1.ql
index 48f6b41e19d..e3c1530fb1a 100644
--- a/csharp/ql/test/library-tests/expressions/AddEventExpr1.ql
+++ b/csharp/ql/test/library-tests/expressions/AddEventExpr1.ql
@@ -9,5 +9,5 @@ where
c.hasName("LoginDialog") and
e.getEnclosingCallable() = c and
e.getTarget().hasName("Click") and
- e.getLValue().getQualifier().(FieldAccess).getTarget().hasName("OkButton")
+ e.getLeftOperand().getQualifier().(FieldAccess).getTarget().hasName("OkButton")
select c, e
diff --git a/csharp/ql/test/library-tests/expressions/AnonymousMethod1.ql b/csharp/ql/test/library-tests/expressions/AnonymousMethod1.ql
index 2c8268e87e1..74e9d3cb1ff 100644
--- a/csharp/ql/test/library-tests/expressions/AnonymousMethod1.ql
+++ b/csharp/ql/test/library-tests/expressions/AnonymousMethod1.ql
@@ -6,7 +6,7 @@ import csharp
from Assignment assign, AnonymousMethodExpr e
where
- assign.getLValue().(VariableAccess).getTarget().hasName("f7") and
+ assign.getLeftOperand().(VariableAccess).getTarget().hasName("f7") and
e.getParent+() = assign and
e.getNumberOfParameters() = 1 and
e.getParameter(0).getType() instanceof IntType and
diff --git a/csharp/ql/test/library-tests/expressions/AnonymousMethod2.ql b/csharp/ql/test/library-tests/expressions/AnonymousMethod2.ql
index e9fbbf01a10..8f0390b0f82 100644
--- a/csharp/ql/test/library-tests/expressions/AnonymousMethod2.ql
+++ b/csharp/ql/test/library-tests/expressions/AnonymousMethod2.ql
@@ -6,7 +6,7 @@ import csharp
from Assignment assign, AnonymousMethodExpr e, Parameter p, ParameterAccess pa
where
- assign.getLValue().(VariableAccess).getTarget().hasName("f7") and
+ assign.getLeftOperand().(VariableAccess).getTarget().hasName("f7") and
e.getParent+() = assign and
e.getNumberOfParameters() = 1 and
p = e.getParameter(0) and
diff --git a/csharp/ql/test/library-tests/expressions/AnonymousMethod3.ql b/csharp/ql/test/library-tests/expressions/AnonymousMethod3.ql
index e4c2e9ae9ba..46d8907319d 100644
--- a/csharp/ql/test/library-tests/expressions/AnonymousMethod3.ql
+++ b/csharp/ql/test/library-tests/expressions/AnonymousMethod3.ql
@@ -6,7 +6,7 @@ import csharp
from Assignment assign, AnonymousMethodExpr e
where
- assign.getLValue().(VariableAccess).getTarget().hasName("f7") and
+ assign.getLeftOperand().(VariableAccess).getTarget().hasName("f7") and
e.getParent+() = assign and
e.getNumberOfParameters() = 1 and
e.getType().(DelegateType).getReturnType() instanceof IntType
diff --git a/csharp/ql/test/library-tests/expressions/AnonymousMethod4.ql b/csharp/ql/test/library-tests/expressions/AnonymousMethod4.ql
index 4d424b65b84..cca81c6b04e 100644
--- a/csharp/ql/test/library-tests/expressions/AnonymousMethod4.ql
+++ b/csharp/ql/test/library-tests/expressions/AnonymousMethod4.ql
@@ -6,7 +6,7 @@ import csharp
from Assignment assign, AnonymousMethodExpr e
where
- assign.getLValue().(VariableAccess).getTarget().hasName("f8") and
+ assign.getLeftOperand().(VariableAccess).getTarget().hasName("f8") and
e.getParent+() = assign and
e.hasNoParameters()
select e, e
diff --git a/csharp/ql/test/library-tests/expressions/AnonymousMethod5.ql b/csharp/ql/test/library-tests/expressions/AnonymousMethod5.ql
index cbc6ac82ca7..577d810dfad 100644
--- a/csharp/ql/test/library-tests/expressions/AnonymousMethod5.ql
+++ b/csharp/ql/test/library-tests/expressions/AnonymousMethod5.ql
@@ -6,7 +6,7 @@ import csharp
from Assignment assign, AnonymousMethodExpr e, LocalVariableAccess va
where
- assign.getLValue().(VariableAccess).getTarget().hasName("f8") and
+ assign.getLeftOperand().(VariableAccess).getTarget().hasName("f8") and
e.getParent+() = assign and
e.hasNoParameters() and
va.getEnclosingStmt().getParent+() = e.getBody() and
diff --git a/csharp/ql/test/library-tests/expressions/AnonymousObjectCreation1.ql b/csharp/ql/test/library-tests/expressions/AnonymousObjectCreation1.ql
index 74d8cd27a94..c717aa260e0 100644
--- a/csharp/ql/test/library-tests/expressions/AnonymousObjectCreation1.ql
+++ b/csharp/ql/test/library-tests/expressions/AnonymousObjectCreation1.ql
@@ -6,11 +6,11 @@ import csharp
from Assignment assign, AnonymousObjectCreation o, Assignment a, Property p
where
- assign.getLValue().(VariableAccess).getTarget().hasName("list2") and
+ assign.getLeftOperand().(VariableAccess).getTarget().hasName("list2") and
o.getParent+() = assign and
o.getInitializer().getMemberInitializer(0) = a and
- a.getRValue().getValue() = "2" and
- p = a.getLValue().(PropertyAccess).getTarget() and
+ a.getRightOperand().getValue() = "2" and
+ p = a.getLeftOperand().(PropertyAccess).getTarget() and
p.hasName("i") and
p.getDeclaringType() = o.getObjectType()
select o
diff --git a/csharp/ql/test/library-tests/expressions/AnonymousObjectCreation2.ql b/csharp/ql/test/library-tests/expressions/AnonymousObjectCreation2.ql
index 5f9e16564b4..d55bf89d606 100644
--- a/csharp/ql/test/library-tests/expressions/AnonymousObjectCreation2.ql
+++ b/csharp/ql/test/library-tests/expressions/AnonymousObjectCreation2.ql
@@ -6,11 +6,11 @@ import csharp
from Assignment assign, AnonymousObjectCreation o, Assignment a, Property p
where
- assign.getLValue().(VariableAccess).getTarget().hasName("contacts2") and
+ assign.getLeftOperand().(VariableAccess).getTarget().hasName("contacts2") and
o.getParent+() = assign and
o.getInitializer().getMemberInitializer(0) = a and
- a.getRValue().getValue() = "Chris Smith" and
- p = a.getLValue().(PropertyAccess).getTarget() and
+ a.getRightOperand().getValue() = "Chris Smith" and
+ p = a.getLeftOperand().(PropertyAccess).getTarget() and
p.hasName("Name") and
p.getDeclaringType() = o.getObjectType()
select o, p.getType().toString()
diff --git a/csharp/ql/test/library-tests/expressions/AnonymousObjectCreation3.ql b/csharp/ql/test/library-tests/expressions/AnonymousObjectCreation3.ql
index afa9ca0d3b2..6033bfed38a 100644
--- a/csharp/ql/test/library-tests/expressions/AnonymousObjectCreation3.ql
+++ b/csharp/ql/test/library-tests/expressions/AnonymousObjectCreation3.ql
@@ -6,11 +6,11 @@ import csharp
from Assignment assign, AnonymousObjectCreation o, Assignment a, Property p
where
- assign.getLValue().(VariableAccess).getTarget().hasName("contacts2") and
+ assign.getLeftOperand().(VariableAccess).getTarget().hasName("contacts2") and
o.getParent+() = assign and
o.getInitializer().getMemberInitializer(1) = a and
- a.getRValue() instanceof ArrayCreation and
- p = a.getLValue().(PropertyAccess).getTarget() and
+ a.getRightOperand() instanceof ArrayCreation and
+ p = a.getLeftOperand().(PropertyAccess).getTarget() and
p.hasName("PhoneNumbers") and
p.getDeclaringType() = o.getObjectType()
select o, p.getType().getName()
diff --git a/csharp/ql/test/library-tests/expressions/AnonymousObjectCreation4.ql b/csharp/ql/test/library-tests/expressions/AnonymousObjectCreation4.ql
index b6354d1f493..a52278839f2 100644
--- a/csharp/ql/test/library-tests/expressions/AnonymousObjectCreation4.ql
+++ b/csharp/ql/test/library-tests/expressions/AnonymousObjectCreation4.ql
@@ -8,7 +8,7 @@ from
Assignment assign, AnonymousObjectCreation o, Assignment a, AnonymousObjectCreation p,
Assignment b
where
- assign.getLValue().(VariableAccess).getTarget().hasName("contacts2") and
+ assign.getLeftOperand().(VariableAccess).getTarget().hasName("contacts2") and
o.getParent+() = assign and
o.getInitializer().getMemberInitializer(1) = a and
p.getParent+() = assign and
diff --git a/csharp/ql/test/library-tests/expressions/ArrayCreation1.ql b/csharp/ql/test/library-tests/expressions/ArrayCreation1.ql
index fba7a403615..6f728347bff 100644
--- a/csharp/ql/test/library-tests/expressions/ArrayCreation1.ql
+++ b/csharp/ql/test/library-tests/expressions/ArrayCreation1.ql
@@ -6,8 +6,8 @@ import csharp
from Assignment a, ArrayCreation e, ArrayInitializer i
where
- a.getLValue().(VariableAccess).getTarget().hasName("is1") and
- e = a.getRValue() and
+ a.getLeftOperand().(VariableAccess).getTarget().hasName("is1") and
+ e = a.getRightOperand() and
not e.isImplicitlyTyped() and
i = e.getInitializer() and
e.isImplicitlySized() and
diff --git a/csharp/ql/test/library-tests/expressions/ArrayCreation10.ql b/csharp/ql/test/library-tests/expressions/ArrayCreation10.ql
index d8a1df12867..951ca22c0c2 100644
--- a/csharp/ql/test/library-tests/expressions/ArrayCreation10.ql
+++ b/csharp/ql/test/library-tests/expressions/ArrayCreation10.ql
@@ -6,9 +6,9 @@ import csharp
from Assignment a, ArrayCreation e, CastExpr cast
where
- a.getLValue().(VariableAccess).getTarget().hasName("os") and
+ a.getLeftOperand().(VariableAccess).getTarget().hasName("os") and
e.getEnclosingCallable().hasName("MainElementAccess") and
- e = a.getRValue() and
+ e = a.getRightOperand() and
not e.isImplicitlyTyped() and
e.isImplicitlySized() and
e.getArrayType().getDimension() = 1 and
diff --git a/csharp/ql/test/library-tests/expressions/ArrayCreation2.ql b/csharp/ql/test/library-tests/expressions/ArrayCreation2.ql
index ae56d579839..88ce79bc91e 100644
--- a/csharp/ql/test/library-tests/expressions/ArrayCreation2.ql
+++ b/csharp/ql/test/library-tests/expressions/ArrayCreation2.ql
@@ -6,8 +6,8 @@ import csharp
from Assignment a, ArrayCreation e, ArrayInitializer i
where
- a.getLValue().(VariableAccess).getTarget().hasName("is2") and
- e = a.getRValue() and
+ a.getLeftOperand().(VariableAccess).getTarget().hasName("is2") and
+ e = a.getRightOperand() and
not e.isImplicitlyTyped() and
i = e.getInitializer() and
e.getNumberOfLengthArguments() = 2 and
diff --git a/csharp/ql/test/library-tests/expressions/ArrayCreation3.ql b/csharp/ql/test/library-tests/expressions/ArrayCreation3.ql
index efe626dab08..0da55f86479 100644
--- a/csharp/ql/test/library-tests/expressions/ArrayCreation3.ql
+++ b/csharp/ql/test/library-tests/expressions/ArrayCreation3.ql
@@ -6,8 +6,8 @@ import csharp
from Assignment a, ArrayCreation e
where
- a.getLValue().(VariableAccess).getTarget().hasName("is3") and
- e = a.getRValue() and
+ a.getLeftOperand().(VariableAccess).getTarget().hasName("is3") and
+ e = a.getRightOperand() and
not e.isImplicitlyTyped() and
not e.hasInitializer() and
e.getNumberOfLengthArguments() = 1 and
diff --git a/csharp/ql/test/library-tests/expressions/ArrayCreation4.ql b/csharp/ql/test/library-tests/expressions/ArrayCreation4.ql
index 2a0dd531283..b79ec3f7bd6 100644
--- a/csharp/ql/test/library-tests/expressions/ArrayCreation4.ql
+++ b/csharp/ql/test/library-tests/expressions/ArrayCreation4.ql
@@ -6,8 +6,8 @@ import csharp
from Assignment a, ArrayCreation e
where
- a.getLValue().(VariableAccess).getTarget().hasName("is4") and
- e = a.getRValue() and
+ a.getLeftOperand().(VariableAccess).getTarget().hasName("is4") and
+ e = a.getRightOperand() and
not e.isImplicitlyTyped() and
not e.hasInitializer() and
e.getNumberOfLengthArguments() = 2 and
diff --git a/csharp/ql/test/library-tests/expressions/ArrayCreation5.ql b/csharp/ql/test/library-tests/expressions/ArrayCreation5.ql
index 04c29cafba8..88df5bef175 100644
--- a/csharp/ql/test/library-tests/expressions/ArrayCreation5.ql
+++ b/csharp/ql/test/library-tests/expressions/ArrayCreation5.ql
@@ -6,8 +6,8 @@ import csharp
from Assignment a, ArrayCreation e, int i
where
- a.getLValue().(VariableAccess).getTarget().hasName("is5") and
- e = a.getRValue() and
+ a.getLeftOperand().(VariableAccess).getTarget().hasName("is5") and
+ e = a.getRightOperand() and
e.isImplicitlyTyped() and
e.isImplicitlySized() and
e.getArrayType().getDimension() = 1 and
diff --git a/csharp/ql/test/library-tests/expressions/ArrayCreation6.ql b/csharp/ql/test/library-tests/expressions/ArrayCreation6.ql
index 7ca6bbe9668..237900bbe7c 100644
--- a/csharp/ql/test/library-tests/expressions/ArrayCreation6.ql
+++ b/csharp/ql/test/library-tests/expressions/ArrayCreation6.ql
@@ -6,8 +6,8 @@ import csharp
from Assignment a, ArrayCreation e
where
- a.getLValue().(VariableAccess).getTarget().hasName("is6") and
- e = a.getRValue() and
+ a.getLeftOperand().(VariableAccess).getTarget().hasName("is6") and
+ e = a.getRightOperand() and
e.isImplicitlyTyped() and
e.isImplicitlySized() and
e.getArrayType().getDimension() = 1 and
diff --git a/csharp/ql/test/library-tests/expressions/ArrayCreation7.ql b/csharp/ql/test/library-tests/expressions/ArrayCreation7.ql
index e34253a4f02..a466195a0b1 100644
--- a/csharp/ql/test/library-tests/expressions/ArrayCreation7.ql
+++ b/csharp/ql/test/library-tests/expressions/ArrayCreation7.ql
@@ -6,8 +6,8 @@ import csharp
from Assignment a, ArrayCreation e
where
- a.getLValue().(VariableAccess).getTarget().hasName("is7") and
- e = a.getRValue() and
+ a.getLeftOperand().(VariableAccess).getTarget().hasName("is7") and
+ e = a.getRightOperand() and
e.isImplicitlyTyped() and
e.isImplicitlySized() and
e.getArrayType().getDimension() = 1 and
diff --git a/csharp/ql/test/library-tests/expressions/ArrayCreation8.ql b/csharp/ql/test/library-tests/expressions/ArrayCreation8.ql
index cc1fd366db1..8eb810247c0 100644
--- a/csharp/ql/test/library-tests/expressions/ArrayCreation8.ql
+++ b/csharp/ql/test/library-tests/expressions/ArrayCreation8.ql
@@ -6,8 +6,8 @@ import csharp
from Assignment a, ArrayCreation e
where
- a.getLValue().(VariableAccess).getTarget().hasName("contacts2") and
- e = a.getRValue() and
+ a.getLeftOperand().(VariableAccess).getTarget().hasName("contacts2") and
+ e = a.getRightOperand() and
e.isImplicitlyTyped() and
e.isImplicitlySized() and
e.getArrayType().getDimension() = 1 and
diff --git a/csharp/ql/test/library-tests/expressions/ArrayCreation9.ql b/csharp/ql/test/library-tests/expressions/ArrayCreation9.ql
index fc4b561c170..55ba1d1edb1 100644
--- a/csharp/ql/test/library-tests/expressions/ArrayCreation9.ql
+++ b/csharp/ql/test/library-tests/expressions/ArrayCreation9.ql
@@ -6,8 +6,8 @@ import csharp
from Assignment a, ArrayCreation e
where
- a.getLValue().(VariableAccess).getTarget().hasName("t") and
- e = a.getRValue() and
+ a.getLeftOperand().(VariableAccess).getTarget().hasName("t") and
+ e = a.getRightOperand() and
e.isImplicitlyTyped() and
e.isImplicitlySized() and
e.getArrayType().getDimension() = 1 and
diff --git a/csharp/ql/test/library-tests/expressions/Lambda1.ql b/csharp/ql/test/library-tests/expressions/Lambda1.ql
index f4787c584f3..4e4d17b9d24 100644
--- a/csharp/ql/test/library-tests/expressions/Lambda1.ql
+++ b/csharp/ql/test/library-tests/expressions/Lambda1.ql
@@ -6,7 +6,7 @@ import csharp
from Assignment assign, LambdaExpr e
where
- assign.getLValue().(VariableAccess).getTarget().hasName("f1") and
+ assign.getLeftOperand().(VariableAccess).getTarget().hasName("f1") and
e.getParent+() = assign and
e.getNumberOfParameters() = 1 and
e.getParameter(0).getType() instanceof ShortType and
diff --git a/csharp/ql/test/library-tests/expressions/Lambda2.ql b/csharp/ql/test/library-tests/expressions/Lambda2.ql
index 5fff4bd2cf6..ff5c06ec670 100644
--- a/csharp/ql/test/library-tests/expressions/Lambda2.ql
+++ b/csharp/ql/test/library-tests/expressions/Lambda2.ql
@@ -6,7 +6,7 @@ import csharp
from Assignment assign, LambdaExpr e
where
- assign.getLValue().(VariableAccess).getTarget().hasName("f2") and
+ assign.getLeftOperand().(VariableAccess).getTarget().hasName("f2") and
e.getParent+() = assign and
e.getNumberOfParameters() = 1 and
e.getParameter(0).getType() instanceof IntType and
diff --git a/csharp/ql/test/library-tests/expressions/Lambda3.ql b/csharp/ql/test/library-tests/expressions/Lambda3.ql
index 32aa919cd20..46d3a411b36 100644
--- a/csharp/ql/test/library-tests/expressions/Lambda3.ql
+++ b/csharp/ql/test/library-tests/expressions/Lambda3.ql
@@ -6,7 +6,7 @@ import csharp
from Assignment assign, LambdaExpr e
where
- assign.getLValue().(VariableAccess).getTarget().hasName("f3") and
+ assign.getLeftOperand().(VariableAccess).getTarget().hasName("f3") and
e.getParent+() = assign and
e.getNumberOfParameters() = 1 and
e.getParameter(0).getType() instanceof IntType and
diff --git a/csharp/ql/test/library-tests/expressions/Lambda4.ql b/csharp/ql/test/library-tests/expressions/Lambda4.ql
index ca7eb7a4207..69ac40ad932 100644
--- a/csharp/ql/test/library-tests/expressions/Lambda4.ql
+++ b/csharp/ql/test/library-tests/expressions/Lambda4.ql
@@ -6,7 +6,7 @@ import csharp
from Assignment assign, LambdaExpr e
where
- assign.getLValue().(VariableAccess).getTarget().hasName("f4") and
+ assign.getLeftOperand().(VariableAccess).getTarget().hasName("f4") and
e.getParent+() = assign and
e.getNumberOfParameters() = 1 and
e.getParameter(0).getType() instanceof IntType and
diff --git a/csharp/ql/test/library-tests/expressions/Lambda5.ql b/csharp/ql/test/library-tests/expressions/Lambda5.ql
index cc577aa85cb..3836ca4effd 100644
--- a/csharp/ql/test/library-tests/expressions/Lambda5.ql
+++ b/csharp/ql/test/library-tests/expressions/Lambda5.ql
@@ -6,7 +6,7 @@ import csharp
from Assignment assign, LambdaExpr e
where
- assign.getLValue().(VariableAccess).getTarget().hasName("f5") and
+ assign.getLeftOperand().(VariableAccess).getTarget().hasName("f5") and
e.getParent+() = assign and
e.getNumberOfParameters() = 2 and
e.getParameter(0).getType() instanceof IntType and
diff --git a/csharp/ql/test/library-tests/expressions/Lambda6.ql b/csharp/ql/test/library-tests/expressions/Lambda6.ql
index c584e4f6c09..4a6ee312834 100644
--- a/csharp/ql/test/library-tests/expressions/Lambda6.ql
+++ b/csharp/ql/test/library-tests/expressions/Lambda6.ql
@@ -6,7 +6,7 @@ import csharp
from Assignment assign, LambdaExpr e
where
- assign.getLValue().(VariableAccess).getTarget().hasName("f6") and
+ assign.getLeftOperand().(VariableAccess).getTarget().hasName("f6") and
e.getParent+() = assign and
e.getNumberOfParameters() = 0 and
e.getType().(DelegateType).hasName("Unit") and
diff --git a/csharp/ql/test/library-tests/expressions/ObjectCreation10.ql b/csharp/ql/test/library-tests/expressions/ObjectCreation10.ql
index abd4a9d6ec6..971654a95b3 100644
--- a/csharp/ql/test/library-tests/expressions/ObjectCreation10.ql
+++ b/csharp/ql/test/library-tests/expressions/ObjectCreation10.ql
@@ -6,7 +6,7 @@ import csharp
from Assignment a, CollectionInitializer i
where
- a.getLValue().(VariableAccess).getTarget().hasName("list1") and
+ a.getLeftOperand().(VariableAccess).getTarget().hasName("list1") and
i.getParent+() = a and
i.getElementInitializer(0).getArgument(0) instanceof AssignExpr
select i.getAChild+()
diff --git a/csharp/ql/test/library-tests/expressions/ObjectCreation11.ql b/csharp/ql/test/library-tests/expressions/ObjectCreation11.ql
index c874735c300..0265579ff67 100644
--- a/csharp/ql/test/library-tests/expressions/ObjectCreation11.ql
+++ b/csharp/ql/test/library-tests/expressions/ObjectCreation11.ql
@@ -6,7 +6,7 @@ import csharp
from Assignment a, CollectionInitializer i, AnonymousObjectCreation o
where
- a.getLValue().(VariableAccess).getTarget().hasName("list2") and
+ a.getLeftOperand().(VariableAccess).getTarget().hasName("list2") and
i.getParent+() = a and
i.getElementInitializer(0).getArgument(0) = o
select i, o
diff --git a/csharp/ql/test/library-tests/expressions/ObjectCreation4.ql b/csharp/ql/test/library-tests/expressions/ObjectCreation4.ql
index 5812397b11b..6ec3e2ec327 100644
--- a/csharp/ql/test/library-tests/expressions/ObjectCreation4.ql
+++ b/csharp/ql/test/library-tests/expressions/ObjectCreation4.ql
@@ -15,9 +15,9 @@ where
cc.hasName("Point") and
i = e.getInitializer() and
a = i.getMemberInitializer(0) and
- a.getLValue().(PropertyAccess).getTarget().hasName("X") and
- a.getRValue().getValue() = "0" and
+ a.getLeftOperand().(PropertyAccess).getTarget().hasName("X") and
+ a.getRightOperand().getValue() = "0" and
b = i.getMemberInitializer(1) and
- b.getLValue().(PropertyAccess).getTarget().hasName("Y") and
- b.getRValue().getValue() = "1"
+ b.getLeftOperand().(PropertyAccess).getTarget().hasName("Y") and
+ b.getRightOperand().getValue() = "1"
select e, i, a, b
diff --git a/csharp/ql/test/library-tests/expressions/ObjectCreation5.ql b/csharp/ql/test/library-tests/expressions/ObjectCreation5.ql
index cf31f518ec7..e130da484d7 100644
--- a/csharp/ql/test/library-tests/expressions/ObjectCreation5.ql
+++ b/csharp/ql/test/library-tests/expressions/ObjectCreation5.ql
@@ -15,10 +15,10 @@ where
cc.hasName("Point") and
i = e.getInitializer() and
a = i.getMemberInitializer(0) and
- a.getLValue().(PropertyAccess).getTarget().hasName("X") and
- a.getRValue().getValue() = "2" and
+ a.getLeftOperand().(PropertyAccess).getTarget().hasName("X") and
+ a.getRightOperand().getValue() = "2" and
b = i.getMemberInitializer(1) and
- b.getLValue().(PropertyAccess).getTarget().hasName("Y") and
- b.getRValue().getValue() = "3" and
+ b.getLeftOperand().(PropertyAccess).getTarget().hasName("Y") and
+ b.getRightOperand().getValue() = "3" and
i.getNumberOfMemberInitializers() = 2
select i, a, b
diff --git a/csharp/ql/test/library-tests/expressions/ObjectCreation6.ql b/csharp/ql/test/library-tests/expressions/ObjectCreation6.ql
index 11e771890ca..529607c8d4b 100644
--- a/csharp/ql/test/library-tests/expressions/ObjectCreation6.ql
+++ b/csharp/ql/test/library-tests/expressions/ObjectCreation6.ql
@@ -15,10 +15,10 @@ where
cc.hasName("Rectangle") and
i = e.getInitializer() and
a = i.getMemberInitializer(0) and
- a.getLValue().(PropertyAccess).getTarget().hasName("P1") and
- a.getRValue() instanceof ObjectCreation and
+ a.getLeftOperand().(PropertyAccess).getTarget().hasName("P1") and
+ a.getRightOperand() instanceof ObjectCreation and
b = i.getMemberInitializer(1) and
- b.getLValue().(PropertyAccess).getTarget().hasName("P2") and
- b.getRValue() instanceof ObjectCreation and
+ b.getLeftOperand().(PropertyAccess).getTarget().hasName("P2") and
+ b.getRightOperand() instanceof ObjectCreation and
i.getNumberOfMemberInitializers() = 2
select i, a, b
diff --git a/csharp/ql/test/library-tests/expressions/ObjectCreation7.ql b/csharp/ql/test/library-tests/expressions/ObjectCreation7.ql
index ccb17515525..404011f1896 100644
--- a/csharp/ql/test/library-tests/expressions/ObjectCreation7.ql
+++ b/csharp/ql/test/library-tests/expressions/ObjectCreation7.ql
@@ -15,10 +15,10 @@ where
cc.hasName("Rectangle2") and
i = e.getInitializer() and
a = i.getMemberInitializer(0) and
- a.getLValue().(PropertyAccess).getTarget().hasName("P1") and
- a.getRValue() instanceof ObjectInitializer and
+ a.getLeftOperand().(PropertyAccess).getTarget().hasName("P1") and
+ a.getRightOperand() instanceof ObjectInitializer and
b = i.getMemberInitializer(1) and
- b.getLValue().(PropertyAccess).getTarget().hasName("P2") and
- b.getRValue() instanceof ObjectInitializer and
+ b.getLeftOperand().(PropertyAccess).getTarget().hasName("P2") and
+ b.getRightOperand() instanceof ObjectInitializer and
i.getNumberOfMemberInitializers() = 2
select m, e
diff --git a/csharp/ql/test/library-tests/expressions/RemoveEventExpr1.ql b/csharp/ql/test/library-tests/expressions/RemoveEventExpr1.ql
index 95b223ed6f4..991fdd6e492 100644
--- a/csharp/ql/test/library-tests/expressions/RemoveEventExpr1.ql
+++ b/csharp/ql/test/library-tests/expressions/RemoveEventExpr1.ql
@@ -9,5 +9,5 @@ where
c.hasName("LoginDialog") and
e.getEnclosingCallable() = c and
e.getTarget().hasName("Click") and
- e.getLValue().getQualifier().(FieldAccess).getTarget().hasName("CancelButton")
+ e.getLeftOperand().getQualifier().(FieldAccess).getTarget().hasName("CancelButton")
select c, e
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 00000000000..9655a5a0fa9
--- /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 00000000000..bc6d464fa3b
--- /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 00000000000..722d8489680
--- /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 00000000000..75c39b4541b
--- /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 981b3600266..01c770d105b 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 28775378f04..b81421da571 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 c0ee8dd0ec7..86bf1476007 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 d6d043f2376..ed5cd137a85 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 b59513504d9..4c85b397ac1 100644
--- a/csharp/ql/test/utils/modelgenerator/dataflow/Summaries.cs
+++ b/csharp/ql/test/utils/modelgenerator/dataflow/Summaries.cs
@@ -536,6 +536,12 @@ public class HigherOrderParameters
{
a(o);
}
+
+ private void CallApply()
+ {
+ // Test that this call to `Apply` does not interfere with the flow summaries generated for `Apply`
+ Apply(x => x, null);
+ }
}
public static class HigherOrderExtensionMethods
diff --git a/docs/codeql/codeql-language-guides/customizing-library-models-for-actions.rst b/docs/codeql/codeql-language-guides/customizing-library-models-for-actions.rst
index 2bf452b5a90..0b78b37359f 100644
--- a/docs/codeql/codeql-language-guides/customizing-library-models-for-actions.rst
+++ b/docs/codeql/codeql-language-guides/customizing-library-models-for-actions.rst
@@ -24,26 +24,26 @@ The CodeQL library for GitHub Actions exposes the following extensible predicate
Customizing data flow and taint tracking:
-- **actionsSourceModel**\(action, version, output, kind, provenance)
-- **actionsSinkModel**\(action, version, input, kind, provenance)
-- **actionsSummaryModel**\(action, version, input, output, kind, provenance)
+- ``actionsSourceModel(action, version, output, kind, provenance)``
+- ``actionsSinkModel(action, version, input, kind, provenance)``
+- ``actionsSummaryModel(action, version, input, output, kind, provenance)``
Customizing Actions-specific analysis:
-- **argumentInjectionSinksDataModel**\(regexp, command_group, argument_group)
-- **contextTriggerDataModel**\(trigger, context_prefix)
-- **externallyTriggerableEventsDataModel**\(event)
-- **immutableActionsDataModel**\(action)
-- **poisonableActionsDataModel**\(action)
-- **poisonableCommandsDataModel**\(regexp)
-- **poisonableLocalScriptsDataModel**\(regexp, group)
-- **repositoryDataModel**\(visibility, default_branch_name)
-- **trustedActionsOwnerDataModel**\(owner)
-- **untrustedEventPropertiesDataModel**\(property, kind)
-- **untrustedGhCommandDataModel**\(cmd_regex, flag)
-- **untrustedGitCommandDataModel**\(cmd_regex, flag)
-- **vulnerableActionsDataModel**\(action, vulnerable_version, vulnerable_sha, fixed_version)
-- **workflowDataModel**\(path, trigger, job, secrets_source, permissions, runner)
+- ``argumentInjectionSinksDataModel(regexp, command_group, argument_group)``
+- ``contextTriggerDataModel(trigger, context_prefix)``
+- ``externallyTriggerableEventsDataModel(event)``
+- ``immutableActionsDataModel(action)``
+- ``poisonableActionsDataModel(action)``
+- ``poisonableCommandsDataModel(regexp)``
+- ``poisonableLocalScriptsDataModel(regexp, group)``
+- ``repositoryDataModel(visibility, default_branch_name)``
+- ``trustedActionsOwnerDataModel(owner)``
+- ``untrustedEventPropertiesDataModel(property, kind)``
+- ``untrustedGhCommandDataModel(cmd_regex, flag)``
+- ``untrustedGitCommandDataModel(cmd_regex, flag)``
+- ``vulnerableActionsDataModel(action, vulnerable_version, vulnerable_sha, fixed_version)``
+- ``workflowDataModel(path, trigger, job, secrets_source, permissions, runner)``
Examples of custom model definitions
------------------------------------
@@ -62,9 +62,9 @@ To allow any Action from the publisher ``octodemo``, such as ``octodemo/3rd-part
.. code-block:: yaml
extensions:
- - addsTo:
+ - addsTo:
pack: codeql/actions-all
- extensible: trustedActionsOwnerDataModel
+ extensible: trustedActionsOwnerDataModel
data:
- ["octodemo"]
diff --git a/docs/codeql/codeql-language-guides/customizing-library-models-for-cpp.rst b/docs/codeql/codeql-language-guides/customizing-library-models-for-cpp.rst
index 29e8be5a4ae..7ca61963227 100644
--- a/docs/codeql/codeql-language-guides/customizing-library-models-for-cpp.rst
+++ b/docs/codeql/codeql-language-guides/customizing-library-models-for-cpp.rst
@@ -58,6 +58,8 @@ The CodeQL library for CPP analysis exposes the following extensible predicates:
- ``sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance)``. This is used to model sources of potentially tainted data. The ``kind`` of the sources defined using this predicate determine which threat model they are associated with. Different threat models can be used to customize the sources used in an analysis. For more information, see ":ref:`Threat models `."
- ``sinkModel(namespace, type, subtypes, name, signature, ext, input, kind, provenance)``. This is used to model sinks where tainted data may be used in a way that makes the code vulnerable.
- ``summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance)``. This is used to model flow through elements.
+- ``barrierModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance)``. This is used to model barriers, which are elements that stop the flow of taint.
+- ``barrierGuardModel(namespace, type, subtypes, name, signature, ext, input, acceptingValue, kind, provenance)``. This is used to model barrier guards, which are elements that can stop the flow of taint depending on a conditional check.
The extensible predicates are populated using the models defined in data extension files.
@@ -75,7 +77,7 @@ This example shows how the CPP query pack models the return value from the ``rea
boost::asio::read_until(socket, recv_buffer, '\0', error);
-We need to add a tuple to the ``sourceModel``\(namespace, type, subtypes, name, signature, ext, output, kind, provenance) extensible predicate by updating a data extension file.
+We need to add a tuple to the ``sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -86,12 +88,11 @@ We need to add a tuple to the ``sourceModel``\(namespace, type, subtypes, name,
data:
- ["boost::asio", "", False, "read_until", "", "", "Argument[*1]", "remote", "manual"]
-Since we are adding a new source, we need to add a tuple to the ``sourceModel`` extensible predicate.
The first five values identify the callable (in this case a free function) to be modeled as a source.
- The first value ``"boost::asio"`` is the namespace name.
-- The second value ``""`` is the name of the type (class) that contains the method. Because we're modelling a free function, the type is left blank.
-- The third value ``False`` is a flag that indicates whether or not the sink also applies to all overrides of the method. For a free function, this should be ``False``.
+- The second value ``""`` is the name of the type (class) that contains the method. Because we're modeling a free function, the type is left blank.
+- The third value ``False`` is a flag that indicates whether or not the model also applies to all overrides of the method. For a free function, this should be ``False``.
- The fourth value ``"read_until"`` is the function name.
- The fifth value is the function input type signature, which can be used to narrow down between functions that have the same name. In this case, we want the model to include all functions in ``boost::asio`` called ``read_until``.
@@ -111,7 +112,7 @@ This example shows how the CPP query pack models the second argument of the ``bo
boost::asio::write(socket, send_buffer, error);
-We need to add a tuple to the ``sinkModel``\(namespace, type, subtypes, name, signature, ext, input, kind, provenance) extensible predicate by updating a data extension file.
+We need to add a tuple to the ``sinkModel(namespace, type, subtypes, name, signature, ext, input, kind, provenance)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -122,12 +123,11 @@ We need to add a tuple to the ``sinkModel``\(namespace, type, subtypes, name, si
data:
- ["boost::asio", "", False, "write", "", "", "Argument[*1]", "remote-sink", "manual"]
-Since we want to add a new sink, we need to add a tuple to the ``sinkModel`` extensible predicate.
The first five values identify the callable (in this case a free function) to be modeled as a sink.
- The first value ``"boost::asio"`` is the namespace name.
-- The second value ``""`` is the name of the type (class) that contains the method. Because we're modelling a free function, the type is left blank.
-- The third value ``False`` is a flag that indicates whether or not the sink also applies to all overrides of the method. For a free function, this should be ``False``.
+- The second value ``""`` is the name of the type (class) that contains the method. Because we're modeling a free function, the type is left blank.
+- The third value ``False`` is a flag that indicates whether or not the model also applies to all overrides of the method. For a free function, this should be ``False``.
- The fourth value ``"write"`` is the function name.
- The fifth value is the function input type signature, which can be used to narrow down between functions that have the same name. In this case, we want the model to include all functions in ``boost::asio`` called ``write``.
@@ -147,7 +147,7 @@ This example shows how the CPP query pack models flow through a function for a s
boost::asio::write(socket, boost::asio::buffer(send_str), error);
-We need to add tuples to the ``summaryModel``\(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance) extensible predicate by updating a data extension file:
+We need to add tuples to the ``summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance)`` extensible predicate by updating a data extension file:
.. code-block:: yaml
@@ -158,13 +158,11 @@ We need to add tuples to the ``summaryModel``\(namespace, type, subtypes, name,
data:
- ["boost::asio", "", False, "buffer", "", "", "Argument[*0]", "ReturnValue", "taint", "manual"]
-Since we are adding flow through a function, we need to add tuples to the ``summaryModel`` extensible predicate.
-
The first five values identify the callable (in this case free function) to be modeled as a summary.
- The first value ``"boost::asio"`` is the namespace name.
-- The second value ``""`` is the name of the type (class) that contains the method. Because we're modelling a free function, the type is left blank.
-- The third value ``False`` is a flag that indicates whether or not the sink also applies to all overrides of the method. For a free function, this should be ``False``.
+- The second value ``""`` is the name of the type (class) that contains the method. Because we're modeling a free function, the type is left blank.
+- The third value ``False`` is a flag that indicates whether or not the model also applies to all overrides of the method. For a free function, this should be ``False``.
- The fourth value ``"buffer"`` is the function name.
- The fifth value is the function input type signature, which can be used to narrow down between functions that have the same name. In this case, we want the model to include all functions in ``boost::asio`` called ``buffer``.
@@ -176,6 +174,86 @@ The remaining values are used to define the input and output specifications, the
- The ninth value ``"taint"`` is the kind of the flow. ``taint`` means that taint is propagated through the call.
- The tenth value ``"manual"`` is the provenance of the summary, which is used to identify the origin of the summary model.
+Example: Taint barrier using the ``mysql_real_escape_string`` function
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This example shows how the CPP query pack models the ``mysql_real_escape_string`` function as a barrier for SQL injection.
+This function escapes special characters in a string for use in an SQL statement, which prevents SQL injection attacks.
+
+.. code-block:: cpp
+
+ char *query = "SELECT * FROM users WHERE name = '%s'";
+ char *name = get_untrusted_input();
+ char *escaped_name = new char[2 * strlen(name) + 1];
+ mysql_real_escape_string(mysql, escaped_name, name, strlen(name)); // The escaped_name is safe for SQL injection.
+ sprintf(query_buffer, query, escaped_name);
+
+We need to add a tuple to the ``barrierModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance)`` extensible predicate by updating a data extension file.
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/cpp-all
+ extensible: barrierModel
+ data:
+ - ["", "", False, "mysql_real_escape_string", "", "", "Argument[*1]", "sql-injection", "manual"]
+
+The first five values identify the callable (in this case a free function) to be modeled as a barrier.
+
+- The first value ``""`` is the namespace name.
+- The second value ``""`` is the name of the type (class) that contains the method. Because we're modeling a free function, the type is left blank.
+- The third value ``False`` is a flag that indicates whether or not the model also applies to all overrides of the method. For a free function, this should be ``False``.
+- The fourth value ``"mysql_real_escape_string"`` is the function name.
+- The fifth value is the function input type signature, which can be used to narrow down between functions that have the same name.
+
+The sixth value should be left empty and is out of scope for this documentation.
+The remaining values are used to define the output specification, the ``kind``, and the ``provenance`` (origin) of the barrier.
+
+- The seventh value ``"Argument[*1]"`` is the output specification, which means in this case that the barrier is the first indirection (or pointed-to value, ``*``) of the second argument (``Argument[1]``) passed to the function.
+- The eighth value ``"sql-injection"`` is the kind of the barrier. The barrier kind is used to define the queries where the barrier is in scope.
+- The ninth value ``"manual"`` is the provenance of the barrier, which is used to identify the origin of the barrier model.
+
+Example: Add a barrier guard
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This example shows how to model a barrier guard that stops the flow of taint when a conditional check is performed on data.
+A barrier guard model is used when a function returns a boolean that indicates whether the data is safe to use.
+Consider a function called ``is_safe`` which returns ``true`` when the data is considered safe.
+
+.. code-block:: cpp
+
+ if (is_safe(user_input)) { // The check guards the use, so the input is safe.
+ mysql_query(user_input); // This is safe.
+ }
+
+We need to add a tuple to the ``barrierGuardModel(namespace, type, subtypes, name, signature, ext, input, acceptingValue, kind, provenance)`` extensible predicate by updating a data extension file.
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/cpp-all
+ extensible: barrierGuardModel
+ data:
+ - ["", "", False, "is_safe", "", "", "Argument[*0]", "true", "sql-injection", "manual"]
+
+The first five values identify the callable (in this case a free function) to be modeled as a barrier guard.
+
+- The first value ``""`` is the namespace name.
+- The second value ``""`` is the name of the type (class) that contains the method. Because we're modeling a free function, the type is left blank.
+- The third value ``False`` is a flag that indicates whether or not the model guard also applies to all overrides of the method. For a free function, this should be ``False``.
+- The fourth value ``"is_safe"`` is the function name.
+- The fifth value is the function input type signature, which can be used to narrow down between functions that have the same name.
+
+The sixth value should be left empty and is out of scope for this documentation.
+The remaining values are used to define the input specification, the ``accepting-value``, the ``kind``, and the ``provenance`` (origin) of the barrier guard.
+
+- The seventh value ``Argument[*0]`` is the input specification (the value being validated). In this case, the first indirection (or pointed-to value, ``*``) of the first argument (``Argument[0]``) passed to the function.
+- The eighth value ``true`` is the accepting value of the barrier guard. This is the value that the conditional check must return for the barrier to apply.
+- The ninth value ``sql-injection`` is the kind of the barrier guard. The barrier guard kind is used to define the queries where the barrier guard is in scope.
+- The tenth value ``manual`` is the provenance of the barrier guard, which is used to identify the origin of the barrier guard.
+
.. _threat-models-cpp:
Threat models
diff --git a/docs/codeql/codeql-language-guides/customizing-library-models-for-csharp.rst b/docs/codeql/codeql-language-guides/customizing-library-models-for-csharp.rst
index 39b5ee30ee4..a4b0e26d1bc 100644
--- a/docs/codeql/codeql-language-guides/customizing-library-models-for-csharp.rst
+++ b/docs/codeql/codeql-language-guides/customizing-library-models-for-csharp.rst
@@ -58,6 +58,8 @@ The CodeQL library for C# analysis exposes the following extensible predicates:
- ``sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance)``. This is used to model sources of potentially tainted data. The ``kind`` of the sources defined using this predicate determine which threat model they are associated with. Different threat models can be used to customize the sources used in an analysis. For more information, see ":ref:`Threat models `."
- ``sinkModel(namespace, type, subtypes, name, signature, ext, input, kind, provenance)``. This is used to model sinks where tainted data may be used in a way that makes the code vulnerable.
- ``summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance)``. This is used to model flow through elements.
+- ``barrierModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance)``. This is used to model barriers, which are elements that stop the flow of taint.
+- ``barrierGuardModel(namespace, type, subtypes, name, signature, ext, input, acceptingValue, kind, provenance)``. This is used to model barrier guards, which are elements that can stop the flow of taint depending on a conditional check.
- ``neutralModel(namespace, type, name, signature, kind, provenance)``. This is similar to a summary model but used to model the flow of values that have only a minor impact on the dataflow analysis. Manual neutral models (those with a provenance such as ``manual`` or ``ai-manual``) can be used to override generated summary models (those with a provenance such as ``df-generated``), so that the summary model will be ignored. Other than that, neutral models have no effect.
The extensible predicates are populated using the models defined in data extension files.
@@ -91,19 +93,18 @@ We need to add a tuple to the ``sinkModel``\(namespace, type, subtypes, name, si
data:
- ["System.Data.SqlClient", "SqlCommand", False, "SqlCommand", "(System.String,System.Data.SqlClient.SqlConnection)", "", "Argument[0]", "sql-injection", "manual"]
-Since we want to add a new sink, we need to add a tuple to the ``sinkModel`` extensible predicate.
The first five values identify the callable (in this case a method) to be modeled as a sink.
- The first value ``System.Data.SqlClient`` is the namespace name.
- The second value ``SqlCommand`` is the name of the class (type) that contains the method.
-- The third value ``False`` is a flag that indicates whether or not the sink also applies to all overrides of the method.
+- The third value ``False`` is a flag that indicates whether or not the model also applies to all overrides of the method.
- The fourth value ``SqlCommand`` is the method name. Constructors are named after the class.
- The fifth value ``(System.String,System.Data.SqlClient.SqlConnection)`` is the method input type signature. The type names must be fully qualified.
The sixth value should be left empty and is out of scope for this documentation.
-The remaining values are used to define the ``access path``, the ``kind``, and the ``provenance`` (origin) of the sink.
+The remaining values are used to define the ``access-path``, the ``kind``, and the ``provenance`` (origin) of the sink.
-- The seventh value ``Argument[0]`` is the ``access path`` to the first argument passed to the method, which means that this is the location of the sink.
+- The seventh value ``Argument[0]`` is the ``access-path`` to the first argument passed to the method, which means that this is the location of the sink.
- The eighth value ``sql-injection`` is the kind of the sink. The sink kind is used to define the queries where the sink is in scope. In this case - the SQL injection queries.
- The ninth value ``manual`` is the provenance of the sink, which is used to identify the origin of the sink.
@@ -119,7 +120,7 @@ This is the ``GetStream`` method in the ``TcpClient`` class, which is located in
...
}
-We need to add a tuple to the ``sourceModel``\(namespace, type, subtypes, name, signature, ext, output, kind, provenance) extensible predicate by updating a data extension file.
+We need to add a tuple to the ``sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -130,18 +131,16 @@ We need to add a tuple to the ``sourceModel``\(namespace, type, subtypes, name,
data:
- ["System.Net.Sockets", "TcpClient", False, "GetStream", "()", "", "ReturnValue", "remote", "manual"]
-
-Since we are adding a new source, we need to add a tuple to the ``sourceModel`` extensible predicate.
The first five values identify the callable (in this case a method) to be modeled as a source.
- The first value ``System.Net.Sockets`` is the namespace name.
- The second value ``TcpClient`` is the name of the class (type) that contains the source.
-- The third value ``False`` is a flag that indicates whether or not the source also applies to all overrides of the method.
+- The third value ``False`` is a flag that indicates whether or not the model also applies to all overrides of the method.
- The fourth value ``GetStream`` is the method name.
- The fifth value ``()`` is the method input type signature.
The sixth value should be left empty and is out of scope for this documentation.
-The remaining values are used to define the ``access path``, the ``kind``, and the ``provenance`` (origin) of the source.
+The remaining values are used to define the ``access-path``, the ``kind``, and the ``provenance`` (origin) of the source.
- The seventh value ``ReturnValue`` is the access path to the return of the method, which means that it is the return value that should be considered a source of tainted input.
- The eighth value ``remote`` is the kind of the source. The source kind is used to define the threat model where the source is in scope. ``remote`` applies to many of the security related queries as it means a remote source of untrusted data. As an example the SQL injection query uses ``remote`` sources. For more information, see ":ref:`Threat models `."
@@ -159,7 +158,7 @@ This pattern covers many of the cases where we need to summarize flow through a
...
}
-We need to add tuples to the ``summaryModel``\(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance) extensible predicate by updating a data extension file:
+We need to add tuples to the ``summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance)`` extensible predicate by updating a data extension file:
.. code-block:: yaml
@@ -171,7 +170,6 @@ We need to add tuples to the ``summaryModel``\(namespace, type, subtypes, name,
- ["System", "String", False, "Concat", "(System.Object,System.Object)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["System", "String", False, "Concat", "(System.Object,System.Object)", "", "Argument[1]", "ReturnValue", "taint", "manual"]
-Since we are adding flow through a method, we need to add tuples to the ``summaryModel`` extensible predicate.
Each tuple defines flow from one argument to the return value.
The first row defines flow from the first argument (``s1`` in the example) to the return value (``t`` in the example) and the second row defines flow from the second argument (``s2`` in the example) to the return value (``t`` in the example).
@@ -180,12 +178,12 @@ These are the same for both of the rows above as we are adding two summaries for
- The first value ``System`` is the namespace name.
- The second value ``String`` is the class (type) name.
-- The third value ``False`` is a flag that indicates whether or not the summary also applies to all overrides of the method.
+- The third value ``False`` is a flag that indicates whether or not the model also applies to all overrides of the method.
- The fourth value ``Concat`` is the method name.
- The fifth value ``(System.Object,System.Object)`` is the method input type signature.
The sixth value should be left empty and is out of scope for this documentation.
-The remaining values are used to define the ``access path``, the ``kind``, and the ``provenance`` (origin) of the summary.
+The remaining values are used to define the ``access-path``, the ``kind``, and the ``provenance`` (origin) of the summary.
- The seventh value is the access path to the input (where data flows from). ``Argument[0]`` is the access path to the first argument (``s1`` in the example) and ``Argument[1]`` is the access path to the second argument (``s2`` in the example).
- The eighth value ``ReturnValue`` is the access path to the output (where data flows to), in this case ``ReturnValue``, which means that the input flows to the return value.
@@ -216,7 +214,7 @@ This example shows how the C# query pack models flow through a method for a simp
...
}
-We need to add a tuple to the ``summaryModel``\(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance) extensible predicate by updating a data extension file:
+We need to add a tuple to the ``summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance)`` extensible predicate by updating a data extension file:
.. code-block:: yaml
@@ -227,7 +225,6 @@ We need to add a tuple to the ``summaryModel``\(namespace, type, subtypes, name,
data:
- ["System", "String", False, "Trim", "()", "", "Argument[this]", "ReturnValue", "taint", "manual"]
-Since we are adding flow through a method, we need to add tuples to the ``summaryModel`` extensible predicate.
Each tuple defines flow from one argument to the return value.
The first row defines flow from the qualifier of the method call (``s1`` in the example) to the return value (``t`` in the example).
@@ -236,12 +233,12 @@ These are the same for both of the rows above as we are adding two summaries for
- The first value ``System`` is the namespace name.
- The second value ``String`` is the class (type) name.
-- The third value ``False`` is a flag that indicates whether or not the summary also applies to all overrides of the method.
+- The third value ``False`` is a flag that indicates whether or not the model also applies to all overrides of the method.
- The fourth value ``Trim`` is the method name.
- The fifth value ``()`` is the method input type signature.
The sixth value should be left empty and is out of scope for this documentation.
-The remaining values are used to define the ``access path``, the ``kind``, and the ``provenance`` (origin) of the summary.
+The remaining values are used to define the ``access-path``, the ``kind``, and the ``provenance`` (origin) of the summary.
- The seventh value is the access path to the input (where data flows from). ``Argument[this]`` is the access path to the qualifier (``s`` in the example).
- The eighth value ``ReturnValue`` is the access path to the output (where data flows to), in this case ``ReturnValue``, which means that the input flows to the return value.
@@ -260,7 +257,7 @@ Here we model flow through higher order methods and collection types, as well as
...
}
-We need to add tuples to the ``summaryModel``\(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance) extensible predicate by updating a data extension file:
+We need to add tuples to the ``summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance)`` extensible predicate by updating a data extension file:
.. code-block:: yaml
@@ -272,20 +269,18 @@ We need to add tuples to the ``summaryModel``\(namespace, type, subtypes, name,
- ["System.Linq", "Enumerable", False, "Select", "(System.Collections.Generic.IEnumerable,System.Func)", "", "Argument[0].Element", "Argument[1].Parameter[0]", "value", "manual"]
- ["System.Linq", "Enumerable", False, "Select", "(System.Collections.Generic.IEnumerable,System.Func)", "", "Argument[1].ReturnValue", "ReturnValue.Element", "value", "manual"]
-
-Since we are adding flow through a method, we need to add tuples to the ``summaryModel`` extensible predicate.
Each tuple defines part of the flow that comprises the total flow through the ``Select`` method.
The first five values identify the callable (in this case a method) to be modeled as a summary.
These are the same for both of the rows above as we are adding two summaries for the same method.
- The first value ``System.Linq`` is the namespace name.
- The second value ``Enumerable`` is the class (type) name.
-- The third value ``False`` is a flag that indicates whether or not the summary also applies to all overrides of the method.
+- The third value ``False`` is a flag that indicates whether or not the model also applies to all overrides of the method.
- The fourth value ``Select`` is the method name, along with the type parameters for the method. The names of the generic type parameters provided in the model must match the names of the generic type parameters in the method signature in the source code.
- The fifth value ``(System.Collections.Generic.IEnumerable,System.Func)`` is the method input type signature. The generics in the signature must match the generics in the method signature in the source code.
The sixth value should be left empty and is out of scope for this documentation.
-The remaining values are used to define the ``access path``, the ``kind``, and the ``provenance`` (origin) of the summary definition.
+The remaining values are used to define the ``access-path``, the ``kind``, and the ``provenance`` (origin) of the summary definition.
- The seventh value is the access path to the ``input`` (where data flows from).
- The eighth value is the access path to the ``output`` (where data flows to).
@@ -307,6 +302,88 @@ For the remaining values for both rows:
That is, the first row specifies that values can flow from the elements of the qualifier enumerable into the first argument of the function provided to ``Select``. The second row specifies that values can flow from the return value of the function to the elements of the enumerable returned from ``Select``.
+Example: Add a barrier for the ``RawUrl`` property
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+This example shows how we can model a property as a barrier for a specific kind of query.
+A barrier model is used to define that the flow of taint stops at the modeled element for the specified kind of query.
+Here we model the getter of the ``RawUrl`` property of the ``HttpRequest`` class as a barrier for URL redirection queries.
+The ``RawUrl`` property returns the raw URL of the current request, which is considered safe for URL redirects because it is the URL of the current request and cannot be manipulated by an attacker.
+
+.. code-block:: csharp
+
+ public static void TaintBarrier(HttpRequest request) {
+ string url = request.RawUrl; // The return value of this property is considered safe for URL redirects.
+ Response.Redirect(url); // This is not a URL redirection vulnerability.
+ }
+
+We need to add a tuple to the ``barrierModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance)`` extensible predicate by updating a data extension file.
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/csharp-all
+ extensible: barrierModel
+ data:
+ - ["System.Web", "HttpRequest", False, "get_RawUrl", "()", "", "ReturnValue", "url-redirection", "manual"]
+
+The first five values identify the callable (in this case the getter of a property) to be modeled as a barrier.
+
+- The first value ``System.Web`` is the namespace name.
+- The second value ``HttpRequest`` is the class (type) name.
+- The third value ``False`` is a flag that indicates whether or not the model also applies to all overrides of the method.
+- The fourth value ``get_RawUrl`` is the method name. Getter and setter methods are named ``get_`` and ``set_`` respectively.
+- The fifth value ``()`` is the method input type signature.
+
+The sixth value should be left empty and is out of scope for this documentation.
+The remaining values are used to define the ``access-path``, the ``kind``, and the ``provenance`` (origin) of the barrier.
+
+- The seventh value ``ReturnValue`` is the access path to the return value of the property getter, which means that the return value is considered safe.
+- The eighth value ``url-redirection`` is the kind of the barrier. The barrier kind is used to define the queries where the barrier is in scope. In this case - the URL redirection queries.
+- The ninth value ``manual`` is the provenance of the barrier, which is used to identify the origin of the barrier.
+
+Example: Add a barrier guard for the ``IsAbsoluteUri`` property
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+This example shows how we can model a property as a barrier guard for a specific kind of query.
+A barrier guard model is used to stop the flow of taint when a conditional check is performed on data.
+Here we model the getter of the ``IsAbsoluteUri`` property of the ``Uri`` class as a barrier guard for URL redirection queries.
+When the ``IsAbsoluteUri`` property returns ``false``, the URL is relative and therefore safe for URL redirects because it cannot redirect to an external site controlled by an attacker.
+
+.. code-block:: csharp
+
+ public static void TaintBarrierGuard(Uri uri) {
+ if (!uri.IsAbsoluteUri) { // The check guards the redirect, so the URL is safe.
+ Response.Redirect(uri.ToString()); // This is not a URL redirection vulnerability.
+ }
+ }
+
+We need to add a tuple to the ``barrierGuardModel(namespace, type, subtypes, name, signature, ext, input, acceptingValue, kind, provenance)`` extensible predicate by updating a data extension file.
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/csharp-all
+ extensible: barrierGuardModel
+ data:
+ - ["System", "Uri", False, "get_IsAbsoluteUri", "()", "", "Argument[this]", "false", "url-redirection", "manual"]
+
+The first five values identify the callable (in this case the getter of a property) to be modeled as a barrier guard.
+
+- The first value ``System`` is the namespace name.
+- The second value ``Uri`` is the class (type) name.
+- The third value ``False`` is a flag that indicates whether or not the model guard also applies to all overrides of the method.
+- The fourth value ``get_IsAbsoluteUri`` is the method name. Getter and setter methods are named ``get_`` and ``set_`` respectively.
+- The fifth value ``()`` is the method input type signature.
+
+The sixth value should be left empty and is out of scope for this documentation.
+The remaining values are used to define the ``access-path``, the ``accepting-value``, the ``kind``, and the ``provenance`` (origin) of the barrier guard.
+
+- The seventh value ``Argument[this]`` is the access path to the input whose flow is blocked. In this case, the qualifier of the property access (``uri`` in the example).
+- The eighth value ``false`` is the accepting value of the barrier guard. This is the value that the conditional check must return for the barrier to apply. In this case, when ``IsAbsoluteUri`` is ``false``, the URL is relative and considered safe.
+- The ninth value ``url-redirection`` is the kind of the barrier guard. The barrier guard kind is used to define the queries where the barrier guard is in scope. In this case - the URL redirection queries.
+- The tenth value ``manual`` is the provenance of the barrier guard, which is used to identify the origin of the barrier guard.
+
Example: Add a ``neutral`` method
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This example shows how we can model a method as being neutral with respect to flow. We will also cover how to model a property by modeling the getter of the ``Now`` property of the ``DateTime`` class as neutral.
@@ -319,7 +396,7 @@ A neutral model is used to define that there is no flow through a method.
...
}
-We need to add a tuple to the ``neutralModel``\(namespace, type, name, signature, kind, provenance) extensible predicate by updating a data extension file.
+We need to add a tuple to the ``neutralModel(namespace, type, name, signature, kind, provenance)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -330,8 +407,6 @@ We need to add a tuple to the ``neutralModel``\(namespace, type, name, signature
data:
- ["System", "DateTime", "get_Now", "()", "summary", "manual"]
-
-Since we are adding a neutral model, we need to add tuples to the ``neutralModel`` extensible predicate.
The first four values identify the callable (in this case the getter of the ``Now`` property) to be modeled as a neutral, the fifth value is the kind, and the sixth value is the provenance (origin) of the neutral.
- The first value ``System`` is the namespace name.
diff --git a/docs/codeql/codeql-language-guides/customizing-library-models-for-go.rst b/docs/codeql/codeql-language-guides/customizing-library-models-for-go.rst
index c5b74ccd73a..2eb9446459f 100644
--- a/docs/codeql/codeql-language-guides/customizing-library-models-for-go.rst
+++ b/docs/codeql/codeql-language-guides/customizing-library-models-for-go.rst
@@ -58,6 +58,8 @@ The CodeQL library for Go analysis exposes the following extensible predicates:
- ``sourceModel(package, type, subtypes, name, signature, ext, output, kind, provenance)``. This is used to model sources of potentially tainted data. The ``kind`` of the sources defined using this predicate determine which threat model they are associated with. Different threat models can be used to customize the sources used in an analysis. For more information, see ":ref:`Threat models `."
- ``sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance)``. This is used to model sinks where tainted data may be used in a way that makes the code vulnerable.
- ``summaryModel(package, type, subtypes, name, signature, ext, input, output, kind, provenance)``. This is used to model flow through elements.
+- ``barrierModel(package, type, subtypes, name, signature, ext, output, kind, provenance)``. This is used to model barriers, which are elements that stop the flow of taint.
+- ``barrierGuardModel(package, type, subtypes, name, signature, ext, input, acceptingValue, kind, provenance)``. This is used to model barrier guards, which are elements that can stop the flow of taint depending on a conditional check.
- ``neutralModel(package, type, name, signature, kind, provenance)``. This is similar to a summary model but used to model the flow of values that have only a minor impact on the dataflow analysis. Manual neutral models (those with a provenance such as ``manual`` or ``ai-manual``) can be used to override generated summary models (those with a provenance such as ``df-generated``), so that the summary model will be ignored. Other than that, neutral models have no effect.
The extensible predicates are populated using the models defined in data extension files.
@@ -91,19 +93,18 @@ We need to add a tuple to the ``sinkModel``\(package, type, subtypes, name, sign
data:
- ["database/sql", "DB", True, "Prepare", "", "", "Argument[0]", "sql-injection", "manual"]
-Since we want to add a new sink, we need to add a tuple to the ``sinkModel`` extensible predicate.
The first five values identify the function (in this case a method) to be modeled as a sink.
- The first value ``database/sql`` is the package name.
- The second value ``DB`` is the name of the type that the method is associated with.
-- The third value ``True`` is a flag that indicates whether or not the sink also applies to subtypes. This includes when the subtype embeds the given type, so that the method or field is promoted to be a method or field of the subtype. For interface methods it also includes types which implement the interface type.
+- The third value ``True`` is a flag that indicates whether or not the model also applies to subtypes. This includes when the subtype embeds the given type, so that the method or field is promoted to be a method or field of the subtype. For interface methods it also includes types which implement the interface type.
- The fourth value ``Prepare`` is the method name.
- The fifth value ``""`` is the input type signature. For Go it should always be an empty string. It is needed for other languages where multiple functions may have the same name and they need to be distinguished by the number and types of the arguments.
The sixth value should be left empty and is out of scope for this documentation.
-The remaining values are used to define the ``access path``, the ``kind``, and the ``provenance`` (origin) of the sink.
+The remaining values are used to define the ``access-path``, the ``kind``, and the ``provenance`` (origin) of the sink.
-- The seventh value ``Argument[0]`` is the ``access path`` to the first argument passed to the method, which means that this is the location of the sink.
+- The seventh value ``Argument[0]`` is the ``access-path`` to the first argument passed to the method, which means that this is the location of the sink.
- The eighth value ``sql-injection`` is the kind of the sink. The sink kind is used to define the queries where the sink is in scope. In this case - the SQL injection queries.
- The ninth value ``manual`` is the provenance of the sink, which is used to identify the origin of the sink.
@@ -120,7 +121,7 @@ This is the ``FormValue`` method of the ``Request`` type which is located in the
}
-We need to add a tuple to the ``sourceModel``\(package, type, subtypes, name, signature, ext, output, kind, provenance) extensible predicate by updating a data extension file.
+We need to add a tuple to the ``sourceModel(package, type, subtypes, name, signature, ext, output, kind, provenance)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -131,18 +132,16 @@ We need to add a tuple to the ``sourceModel``\(package, type, subtypes, name, si
data:
- ["net/http", "Request", True, "FormValue", "", "", "ReturnValue", "remote", "manual"]
-
-Since we are adding a new source, we need to add a tuple to the ``sourceModel`` extensible predicate.
The first five values identify the function to be modeled as a source.
- The first value ``net/http`` is the package name.
- The second value ``Request`` is the type name, since the function is a method of the ``Request`` type.
-- The third value ``True`` is a flag that indicates whether or not the sink also applies to subtypes. This includes when the subtype embeds the given type, so that the method or field is promoted to be a method or field of the subtype. For interface methods it also includes types which implement the interface type.
+- The third value ``True`` is a flag that indicates whether or not the model also applies to subtypes. This includes when the subtype embeds the given type, so that the method or field is promoted to be a method or field of the subtype. For interface methods it also includes types which implement the interface type.
- The fourth value ``FormValue`` is the function name.
- The fifth value ``""`` is the input type signature. For Go it should always be an empty string. It is needed for other languages where multiple functions may have the same name and they need to be distinguished by the number and types of the arguments.
The sixth value should be left empty and is out of scope for this documentation.
-The remaining values are used to define the ``access path``, the ``kind``, and the ``provenance`` (origin) of the source.
+The remaining values are used to define the ``access-path``, the ``kind``, and the ``provenance`` (origin) of the source.
- The seventh value ``ReturnValue`` is the access path to the return of the method, which means that it is the return value that should be considered a source of tainted input.
- The eighth value ``remote`` is the kind of the source. The source kind is used to define the threat model where the source is in scope. ``remote`` applies to many of the security related queries as it means a remote source of untrusted data. As an example the SQL injection query uses ``remote`` sources. For more information, see ":ref:`Threat models `."
@@ -162,7 +161,7 @@ This pattern covers many of the cases where we need to summarize flow through a
...
}
-We need to add a tuple to the ``summaryModel``\(package, type, subtypes, name, signature, ext, input, output, kind, provenance) extensible predicate by updating a data extension file:
+We need to add a tuple to the ``summaryModel(package, type, subtypes, name, signature, ext, input, output, kind, provenance)`` extensible predicate by updating a data extension file:
.. code-block:: yaml
@@ -171,21 +170,20 @@ We need to add a tuple to the ``summaryModel``\(package, type, subtypes, name, s
pack: codeql/go-all
extensible: summaryModel
data:
- - ["slices", "", False, "Max", "", "", "Argument[0].ArrayElement", "ReturnValue", "value", "manual"]
+ - ["slices", "", False, "Max", "", "", "Argument[0].ArrayElement", "ReturnValue", "value", "manual"]
-Since we are adding flow through a method, we need to add tuples to the ``summaryModel`` extensible predicate.
The first row defines flow from the first argument (``a`` in the example) to the return value (``max`` in the example).
The first five values identify the function to be modeled as a summary.
- The first value ``slices`` is the package name.
- The second value ``""`` is left blank, since the function is not a method of a type.
-- The third value ``False`` is a flag that indicates whether or not the sink also applies to subtypes. This has no effect for non-method functions.
+- The third value ``False`` is a flag that indicates whether or not the model also applies to subtypes. This has no effect for non-method functions.
- The fourth value ``Max`` is the function name.
- The fifth value ``""`` is the input type signature. For Go it should always be an empty string. It is needed for other languages where multiple functions may have the same name and they need to be distinguished by the number and types of the arguments.
The sixth value should be left empty and is out of scope for this documentation.
-The remaining values are used to define the ``access path``, the ``kind``, and the ``provenance`` (origin) of the summary.
+The remaining values are used to define the ``access-path``, the ``kind``, and the ``provenance`` (origin) of the summary.
- The seventh value is the access path to the input (where data flows from). ``Argument[0].ArrayElement`` is the access path to the array elements of the first argument (the elements of the slice in the example).
- The eighth value ``ReturnValue`` is the access path to the output (where data flows to), in this case ``ReturnValue``, which means that the input flows to the return value.
@@ -207,7 +205,7 @@ This pattern covers many of the cases where we need to summarize flow through a
...
}
-We need to add a tuple to the ``summaryModel``\(package, type, subtypes, name, signature, ext, input, output, kind, provenance) extensible predicate by updating a data extension file:
+We need to add a tuple to the ``summaryModel(package, type, subtypes, name, signature, ext, input, output, kind, provenance)`` extensible predicate by updating a data extension file:
.. code-block:: yaml
@@ -218,19 +216,18 @@ We need to add a tuple to the ``summaryModel``\(package, type, subtypes, name, s
data:
- ["slices", "", False, "Concat", "", "", "Argument[0].ArrayElement.ArrayElement", "ReturnValue.ArrayElement", "value", "manual"]
-Since we are adding flow through a method, we need to add tuples to the ``summaryModel`` extensible predicate.
The first row defines flow from the arguments (``a`` and ``b`` in the example) to the return value (``c`` in the example).
The first five values identify the function to be modeled as a summary.
- The first value ``slices`` is the package name.
- The second value ``""`` is left blank, since the function is not a method of a type.
-- The third value ``False`` is a flag that indicates whether or not the sink also applies to subtypes. This has no effect for non-method functions.
+- The third value ``False`` is a flag that indicates whether or not the model also applies to subtypes. This has no effect for non-method functions.
- The fourth value ``Max`` is the function name.
- The fifth value ``""`` is the input type signature. For Go it should always be an empty string. It is needed for other languages where multiple functions may have the same name and they need to be distinguished by the number and types of the arguments.
The sixth value should be left empty and is out of scope for this documentation.
-The remaining values are used to define the ``access path``, the ``kind``, and the ``provenance`` (origin) of the summary.
+The remaining values are used to define the ``access-path``, the ``kind``, and the ``provenance`` (origin) of the summary.
- The seventh value is the access path to the input (where data flows from). ``Argument[0].ArrayElement.ArrayElement`` is the access path to the array elements of the array elements of the first argument. Note that a variadic parameter of type `...T` is treated as if it has type `[]T` and arguments corresponding to the variadic parameter are accessed as elements of this slice.
- The eighth value ``ReturnValue.ArrayElement`` is the access path to the output (where data flows to), in this case ``ReturnValue.ArrayElement``, which means that the input flows to the array elements of the return value.
@@ -251,7 +248,7 @@ This pattern covers many of the cases where we need to summarize flow through a
...
}
-We need to add tuples to the ``summaryModel``\(package, type, subtypes, name, signature, ext, input, output, kind, provenance) extensible predicate by updating a data extension file:
+We need to add tuples to the ``summaryModel(package, type, subtypes, name, signature, ext, input, output, kind, provenance)`` extensible predicate by updating a data extension file:
.. code-block:: yaml
@@ -263,7 +260,6 @@ We need to add tuples to the ``summaryModel``\(package, type, subtypes, name, si
- ["strings", "", False, "Join", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["strings", "", False, "Join", "", "", "Argument[1]", "ReturnValue", "taint", "manual"]
-Since we are adding flow through a method, we need to add tuples to the ``summaryModel`` extensible predicate.
Each tuple defines flow from one argument to the return value.
The first row defines flow from the first argument (``elems`` in the example) to the return value (``t`` in the example) and the second row defines flow from the second argument (``sep`` in the example) to the return value (``t`` in the example).
@@ -272,12 +268,12 @@ These are the same for both of the rows above as we are adding two summaries for
- The first value ``strings`` is the package name.
- The second value ``""`` is left blank, since the function is not a method of a type.
-- The third value ``False`` is a flag that indicates whether or not the sink also applies to subtypes. This has no effect for non-method functions.
+- The third value ``False`` is a flag that indicates whether or not the model also applies to subtypes. This has no effect for non-method functions.
- The fourth value ``Join`` is the function name.
- The fifth value ``""`` is the input type signature. For Go it should always be an empty string. It is needed for other languages where multiple functions may have the same name and they need to be distinguished by the number and types of the arguments.
The sixth value should be left empty and is out of scope for this documentation.
-The remaining values are used to define the ``access path``, the ``kind``, and the ``provenance`` (origin) of the summary.
+The remaining values are used to define the ``access-path``, the ``kind``, and the ``provenance`` (origin) of the summary.
- The seventh value is the access path to the input (where data flows from). ``Argument[0]`` is the access path to the first argument (``elems`` in the example) and ``Argument[1]`` is the access path to the second argument (``sep`` in the example).
- The eighth value ``ReturnValue`` is the access path to the output (where data flows to), in this case ``ReturnValue``, which means that the input flows to the return value.
@@ -307,8 +303,8 @@ This example shows how the Go query pack models flow through a method for a simp
host := u.Hostname() // There is taint flow from u to host.
...
}
-
-We need to add a tuple to the ``summaryModel``\(package, type, subtypes, name, signature, ext, input, output, kind, provenance) extensible predicate by updating a data extension file:
+
+We need to add a tuple to the ``summaryModel(package, type, subtypes, name, signature, ext, input, output, kind, provenance)`` extensible predicate by updating a data extension file:
.. code-block:: yaml
@@ -319,7 +315,6 @@ We need to add a tuple to the ``summaryModel``\(package, type, subtypes, name, s
data:
- ["net/url", "URL", True, "Hostname", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
-Since we are adding flow through a method, we need to add tuples to the ``summaryModel`` extensible predicate.
Each tuple defines flow from one argument to the return value.
The first row defines flow from the qualifier of the method call (``u`` in the example) to the return value (``host`` in the example).
@@ -327,18 +322,98 @@ The first five values identify the function (in this case a method) to be modele
- The first value ``net/url`` is the package name.
- The second value ``URL`` is the receiver type.
-- The third value ``True`` is a flag that indicates whether or not the sink also applies to subtypes. This includes when the subtype embeds the given type, so that the method or field is promoted to be a method or field of the subtype. For interface methods it also includes types which implement the interface type.
+- The third value ``True`` is a flag that indicates whether or not the model also applies to subtypes. This includes when the subtype embeds the given type, so that the method or field is promoted to be a method or field of the subtype. For interface methods it also includes types which implement the interface type.
- The fourth value ``Hostname`` is the method name.
- The fifth value ``""`` is the input type signature. For Go it should always be an empty string. It is needed for other languages where multiple functions may have the same name and they need to be distinguished by the number and types of the arguments.
The sixth value should be left empty and is out of scope for this documentation.
-The remaining values are used to define the ``access path``, the ``kind``, and the ``provenance`` (origin) of the summary.
+The remaining values are used to define the ``access-path``, the ``kind``, and the ``provenance`` (origin) of the summary.
- The seventh value is the access path to the input (where data flows from). ``Argument[receiver]`` is the access path to the receiver (``u`` in the example).
- The eighth value ``ReturnValue`` is the access path to the output (where data flows to), in this case ``ReturnValue``, which means that the input flows to the return value. When there are multiple return values, use ``ReturnValue[i]`` to refer to the ``i`` th return value (starting from 0).
- The ninth value ``taint`` is the kind of the flow. ``taint`` means that taint is propagated through the call.
- The tenth value ``manual`` is the provenance of the summary, which is used to identify the origin of the summary.
+Example: Add a barrier using the ``Htmlquote`` function
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+This example shows how the Go query pack models a barrier that stops the flow of taint.
+The ``Htmlquote`` function from the `beego` framework HTML-escapes a string, which prevents HTML injection attacks.
+
+.. code-block:: go
+
+ func Render(w http.ResponseWriter, r *http.Request) {
+ name := r.FormValue("name")
+ safe := beego.Htmlquote(name) // The return value of this function is safe to use in HTML.
+ ...
+ }
+
+We need to add a tuple to the ``barrierModel(package, type, subtypes, name, signature, ext, output, kind, provenance)`` extensible predicate by updating a data extension file.
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: barrierModel
+ data:
+ - ["group:beego", "", True, "Htmlquote", "", "", "ReturnValue", "html-injection", "manual"]
+
+The first five values identify the function to be modeled as a barrier.
+
+- The first value ``group:beego`` is the package group name. The ``group:`` prefix indicates that this is a package group, which is used to match multiple package paths that refer to the same package.
+- The second value ``""`` is left blank since the function is not a method of a type.
+- The third value ``True`` is a flag that indicates whether or not the model also applies to subtypes. This has no effect for non-method functions.
+- The fourth value ``Htmlquote`` is the function name.
+- The fifth value ``""`` is the input type signature. For Go it should always be an empty string.
+
+The sixth value should be left empty and is out of scope for this documentation.
+The remaining values are used to define the ``access-path``, the ``kind``, and the ``provenance`` (origin) of the barrier.
+
+- The seventh value ``ReturnValue`` is the access path to the output of the barrier, which means that the return value is considered sanitized.
+- The eighth value ``html-injection`` is the kind of the barrier. The barrier kind must match the kind used in the query where the barrier should take effect. In this case, it matches the ``html-injection`` sink kind used by XSS queries.
+- The ninth value ``manual`` is the provenance of the barrier, which is used to identify the origin of the barrier.
+
+Example: Add a barrier guard
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+This example shows how to model a barrier guard that stops the flow of taint when a conditional check is performed on data.
+A barrier guard model is used when a function returns a boolean that indicates whether the data is safe to use.
+Consider a function called ``IsSafe`` which returns ``true`` when the data is considered safe for SQL injection.
+
+.. code-block:: go
+
+ func Query(db *sql.DB, input string) {
+ if example.IsSafe(input) { // The check guards the query, so the input is safe.
+ db.Query(input) // This is not a SQL injection vulnerability.
+ }
+ }
+
+We need to add a tuple to the ``barrierGuardModel(package, type, subtypes, name, signature, ext, input, acceptingValue, kind, provenance)`` extensible predicate by updating a data extension file.
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/go-all
+ extensible: barrierGuardModel
+ data:
+ - ["example.com/example", "", False, "IsSafe", "", "", "Argument[0]", "true", "sql-injection", "manual"]
+
+The first five values identify the function to be modeled as a barrier guard.
+
+- The first value ``example.com/example`` is the package name.
+- The second value ``""`` is left blank since the function is not a method of a type.
+- The third value ``False`` is a flag that indicates whether or not the model guard also applies to subtypes. This has no effect for non-method functions.
+- The fourth value ``IsSafe`` is the function name.
+- The fifth value ``""`` is the input type signature. For Go it should always be an empty string.
+
+The sixth value should be left empty and is out of scope for this documentation.
+The remaining values are used to define the ``access-path``, the ``accepting-value``, the ``kind``, and the ``provenance`` (origin) of the barrier guard.
+
+- The seventh value ``Argument[0]`` is the access path to the input whose flow is blocked. In this case, the first argument to the function (``input`` in the example).
+- The eighth value ``true`` is the accepting value of the barrier guard. This is the value that the conditional check must return for the barrier to apply. In this case, when ``IsSafe`` returns ``true``, the input is considered safe.
+- The ninth value ``sql-injection`` is the kind of the barrier guard. The barrier guard kind is used to define the queries where the barrier guard is in scope. In this case - the SQL injection queries.
+- The tenth value ``manual`` is the provenance of the barrier guard, which is used to identify the origin of the barrier guard.
+
Example: Accessing the ``Body`` field of an HTTP request
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This example shows how we can model a field read as a source of tainted data.
@@ -350,7 +425,7 @@ This example shows how we can model a field read as a source of tainted data.
...
}
-We need to add a tuple to the ``sourceModel``\(package, type, subtypes, name, signature, ext, output, kind, provenance) extensible predicate by updating a data extension file.
+We need to add a tuple to the ``sourceModel(package, type, subtypes, name, signature, ext, output, kind, provenance)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -361,17 +436,16 @@ We need to add a tuple to the ``sourceModel``\(package, type, subtypes, name, si
data:
- ["net/http", "Request", True, "Body", "", "", "", "remote", "manual"]
-Since we are adding a new source, we need to add a tuple to the ``sourceModel`` extensible predicate.
The first five values identify the field to be modeled as a source.
- The first value ``net/http`` is the package name.
- The second value ``Request`` is the name of the type that the field is associated with.
-- The third value ``True`` is a flag that indicates whether or not the sink also applies to subtypes. For fields this means when the field is accessed as a promoted field in another type.
+- The third value ``True`` is a flag that indicates whether or not the model also applies to subtypes. For fields this means when the field is accessed as a promoted field in another type.
- The fourth value ``Body`` is the field name.
- The fifth value ``""`` is the input type signature. For Go it should always be an empty string. It is needed for other languages where multiple functions may have the same name and they need to be distinguished by the number and types of the arguments.
The sixth value should be left empty and is out of scope for this documentation.
-The remaining values are used to define the ``access path``, the ``kind``, and the ``provenance`` (origin) of the source.
+The remaining values are used to define the ``access-path``, the ``kind``, and the ``provenance`` (origin) of the source.
- The seventh value ``""`` is left blank. Leaving the access path of a source model blank indicates that it is a field access.
- The eighth value ``remote`` is the source kind. This indicates that the source is a remote source of untrusted data.
@@ -387,7 +461,7 @@ Note that packages hosted at ``gopkg.in`` use a slightly different syntax: the m
To write models that only apply to ``github.com/couchbase/gocb/v2``, it is sufficient to include the major version suffix (``/v2``) in the package column. To write models that only apply to ``github.com/couchbase/gocb``, you may prefix the package column with ``fixed-version:``. For example, here are two models for a method that has changed name from v1 to v2.
.. code-block:: yaml
-
+
extensions:
- addsTo:
pack: codeql/go-all
diff --git a/docs/codeql/codeql-language-guides/customizing-library-models-for-java-and-kotlin.rst b/docs/codeql/codeql-language-guides/customizing-library-models-for-java-and-kotlin.rst
index 7f0a41b3040..203213b9425 100644
--- a/docs/codeql/codeql-language-guides/customizing-library-models-for-java-and-kotlin.rst
+++ b/docs/codeql/codeql-language-guides/customizing-library-models-for-java-and-kotlin.rst
@@ -63,6 +63,8 @@ The CodeQL library for Java and Kotlin analysis exposes the following extensible
- ``sourceModel(package, type, subtypes, name, signature, ext, output, kind, provenance)``. This is used to model sources of potentially tainted data. The ``kind`` of the sources defined using this predicate determine which threat model they are associated with. Different threat models can be used to customize the sources used in an analysis. For more information, see ":ref:`Threat models `."
- ``sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance)``. This is used to model sinks where tainted data maybe used in a way that makes the code vulnerable.
- ``summaryModel(package, type, subtypes, name, signature, ext, input, output, kind, provenance)``. This is used to model flow through elements.
+- ``barrierModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance)``. This is used to model barriers, which are elements that stop the flow of taint.
+- ``barrierGuardModel(namespace, type, subtypes, name, signature, ext, input, acceptingValue, kind, provenance)``. This is used to model barrier guards, which are elements that can stop the flow of taint depending on a conditional check.
- ``neutralModel(package, type, name, signature, kind, provenance)``. This is similar to a summary model but used to model the flow of values that have only a minor impact on the dataflow analysis. Manual neutral models (those with a provenance such as ``manual`` or ``ai-manual``) override generated summary models (those with a provenance such as ``df-generated``) so that the summary will be ignored. Other than that, neutral models have a slight impact on the dataflow dispatch logic, which is out of scope for this documentation.
The extensible predicates are populated using the models defined in data extension files.
@@ -85,7 +87,7 @@ This is the ``execute`` method in the ``Statement`` class, which is located in t
stmt.execute(query); // The argument to this method is a SQL injection sink.
}
-We need to add a tuple to the ``sinkModel``\(package, type, subtypes, name, signature, ext, input, kind, provenance) extensible predicate by updating a data extension file.
+We need to add a tuple to the ``sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -96,20 +98,18 @@ We need to add a tuple to the ``sinkModel``\(package, type, subtypes, name, sign
data:
- ["java.sql", "Statement", True, "execute", "(String)", "", "Argument[0]", "sql-injection", "manual"]
-
-Since we want to add a new sink, we need to add a tuple to the ``sinkModel`` extensible predicate.
The first five values identify the callable (in this case a method) to be modeled as a sink.
- The first value ``java.sql`` is the package name.
- The second value ``Statement`` is the name of the class (type) that contains the method.
-- The third value ``True`` is a flag that indicates whether or not the sink also applies to all overrides of the method.
+- The third value ``True`` is a flag that indicates whether or not the model also applies to all overrides of the method.
- The fourth value ``execute`` is the method name.
- The fifth value ``(String)`` is the method input type signature.
The sixth value should be left empty and is out of scope for this documentation.
-The remaining values are used to define the ``access path``, the ``kind``, and the ``provenance`` (origin) of the sink.
+The remaining values are used to define the ``access-path``, the ``kind``, and the ``provenance`` (origin) of the sink.
-- The seventh value ``Argument[0]`` is the ``access path`` to the first argument passed to the method, which means that this is the location of the sink.
+- The seventh value ``Argument[0]`` is the ``access-path`` to the first argument passed to the method, which means that this is the location of the sink.
- The eighth value ``sql-injection`` is the kind of the sink. The sink kind is used to define the queries where the sink is in scope. In this case - the SQL injection queries.
- The ninth value ``manual`` is the provenance of the sink, which is used to identify the origin of the sink.
@@ -125,7 +125,7 @@ This is the ``getInputStream`` method in the ``Socket`` class, which is located
...
}
-We need to add a tuple to the ``sourceModel``\(package, type, subtypes, name, signature, ext, output, kind, provenance) extensible predicate by updating a data extension file.
+We need to add a tuple to the ``sourceModel(package, type, subtypes, name, signature, ext, output, kind, provenance)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -136,18 +136,16 @@ We need to add a tuple to the ``sourceModel``\(package, type, subtypes, name, si
data:
- ["java.net", "Socket", False, "getInputStream", "()", "", "ReturnValue", "remote", "manual"]
-
-Since we are adding a new source, we need to add a tuple to the ``sourceModel`` extensible predicate.
The first five values identify the callable (in this case a method) to be modeled as a source.
- The first value ``java.net`` is the package name.
- The second value ``Socket`` is the name of the class (type) that contains the source.
-- The third value ``False`` is a flag that indicates whether or not the source also applies to all overrides of the method.
+- The third value ``False`` is a flag that indicates whether or not the model also applies to all overrides of the method.
- The fourth value ``getInputStream`` is the method name.
- The fifth value ``()`` is the method input type signature.
The sixth value should be left empty and is out of scope for this documentation.
-The remaining values are used to define the ``access path``, the ``kind``, and the ``provenance`` (origin) of the source.
+The remaining values are used to define the ``access-path``, the ``kind``, and the ``provenance`` (origin) of the source.
- The seventh value ``ReturnValue`` is the access path to the return of the method, which means that it is the return value that should be considered a source of tainted input.
- The eighth value ``remote`` is the kind of the source. The source kind is used to define the threat model where the source is in scope. ``remote`` applies to many of the security related queries as it means a remote source of untrusted data. As an example the SQL injection query uses ``remote`` sources. For more information, see ":ref:`Threat models `."
@@ -165,7 +163,7 @@ This pattern covers many of the cases where we need to summarize flow through a
...
}
-We need to add tuples to the ``summaryModel``\(package, type, subtypes, name, signature, ext, input, output, kind, provenance) extensible predicate by updating a data extension file:
+We need to add tuples to the ``summaryModel(package, type, subtypes, name, signature, ext, input, output, kind, provenance)`` extensible predicate by updating a data extension file:
.. code-block:: yaml
@@ -177,7 +175,6 @@ We need to add tuples to the ``summaryModel``\(package, type, subtypes, name, si
- ["java.lang", "String", False, "concat", "(String)", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["java.lang", "String", False, "concat", "(String)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
-Since we are adding flow through a method, we need to add tuples to the ``summaryModel`` extensible predicate.
Each tuple defines flow from one argument to the return value.
The first row defines flow from the qualifier (``s1`` in the example) to the return value (``t`` in the example) and the second row defines flow from the first argument (``s2`` in the example) to the return value (``t`` in the example).
@@ -186,12 +183,12 @@ These are the same for both of the rows above as we are adding two summaries for
- The first value ``java.lang`` is the package name.
- The second value ``String`` is the class (type) name.
-- The third value ``False`` is a flag that indicates whether or not the summary also applies to all overrides of the method.
+- The third value ``False`` is a flag that indicates whether or not the model also applies to all overrides of the method.
- The fourth value ``concat`` is the method name.
- The fifth value ``(String)`` is the method input type signature.
The sixth value should be left empty and is out of scope for this documentation.
-The remaining values are used to define the ``access path``, the ``kind``, and the ``provenance`` (origin) of the summary.
+The remaining values are used to define the ``access-path``, the ``kind``, and the ``provenance`` (origin) of the summary.
- The seventh value is the access path to the input (where data flows from). ``Argument[this]`` is the access path to the qualifier (``s1`` in the example) and ``Argument[0]`` is the access path to the first argument (``s2`` in the example).
- The eighth value ``ReturnValue`` is the access path to the output (where data flows to), in this case ``ReturnValue``, which means that the input flows to the return value.
@@ -210,7 +207,7 @@ Here we model flow through higher order methods and collection types.
...
}
-We need to add tuples to the ``summaryModel``\(package, type, subtypes, name, signature, ext, input, output, kind, provenance) extensible predicate by updating a data extension file:
+We need to add tuples to the ``summaryModel(package, type, subtypes, name, signature, ext, input, output, kind, provenance)`` extensible predicate by updating a data extension file:
.. code-block:: yaml
@@ -222,20 +219,18 @@ We need to add tuples to the ``summaryModel``\(package, type, subtypes, name, si
- ["java.util.stream", "Stream", True, "map", "(Function)", "", "Argument[this].Element", "Argument[0].Parameter[0]", "value", "manual"]
- ["java.util.stream", "Stream", True, "map", "(Function)", "", "Argument[0].ReturnValue", "ReturnValue.Element", "value", "manual"]
-
-Since we are adding flow through a method, we need to add tuples to the ``summaryModel`` extensible predicate.
Each tuple defines part of the flow that comprises the total flow through the ``map`` method.
The first five values identify the callable (in this case a method) to be modeled as a summary.
These are the same for both of the rows above as we are adding two summaries for the same method.
- The first value ``java.util.stream`` is the package name.
- The second value ``Stream`` is the class (type) name.
-- The third value ``True`` is a flag that indicates whether or not the summary also applies to all overrides of the method.
+- The third value ``True`` is a flag that indicates whether or not the model also applies to all overrides of the method.
- The fourth value ``map`` is the method name.
- The fifth value ``Function`` is the method input type signature.
The sixth value should be left empty and is out of scope for this documentation.
-The remaining values are used to define the ``access path``, the ``kind``, and the ``provenance`` (origin) of the summary definition.
+The remaining values are used to define the ``access-path``, the ``kind``, and the ``provenance`` (origin) of the summary definition.
- The seventh value is the access path to the ``input`` (where data flows from).
- The eighth value is the access path to the ``output`` (where data flows to).
@@ -257,6 +252,87 @@ For the remaining values for both rows:
That is, the first row specifies that values can flow from the elements of the qualifier stream into the first argument of the function provided to ``map``. The second row specifies that values can flow from the return value of the function to the elements of the stream returned from ``map``.
+Example: Taint barrier in the ``java.io`` package
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+This example shows how the Java query pack models the return value from the ``getName`` method as a barrier for path injection.
+This is the ``getName`` method in the ``File`` class, which is located in the ``java.io`` package. The method returns only the final component of a path, which means that it protects against path injection vulnerabilities.
+
+.. code-block:: java
+
+ public static void barrier(File file) {
+ String name = file.getName(); // The return value of this method is a barrier for path injection.
+ ...
+ }
+
+We need to add a tuple to the ``barrierModel(package, type, subtypes, name, signature, ext, output, kind, provenance)`` extensible predicate by updating a data extension file.
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/java-all
+ extensible: barrierModel
+ data:
+ - ["java.io", "File", True, "getName", "()", "", "ReturnValue", "path-injection", "manual"]
+
+The first five values identify the callable (in this case a method) to be modeled as a barrier.
+
+- The first value ``java.io`` is the package name.
+- The second value ``File`` is the name of the class (type) that contains the method.
+- The third value ``True`` is a flag that indicates whether or not the model also applies to all overrides of the method.
+- The fourth value ``getName`` is the method name.
+- The fifth value ``()`` is the method input type signature.
+
+The sixth value should be left empty and is out of scope for this documentation.
+The remaining values are used to define the ``access-path``, the ``kind``, and the ``provenance`` (origin) of the barrier.
+
+- The seventh value ``ReturnValue`` is the access path to the return of the method, which means that it is the return value that should be considered a barrier.
+- The eighth value ``path-injection`` is the kind of the barrier. The barrier kind is used to define the queries where the barrier is in scope. In this case - the path injection queries.
+- The ninth value ``manual`` is the provenance of the barrier, which is used to identify the origin of the barrier.
+
+Example: Taint barrier guard in the ``java.net`` package
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+This example shows how the Java query pack models the ``isAbsolute`` method as a barrier guard for request forgery.
+This is the ``isAbsolute`` method in the ``URI`` class, which is located in the ``java.net`` package.
+A barrier guard model is used to stop the flow of taint when a conditional check is performed on data.
+When the ``isAbsolute`` method returns ``false``, the URI is relative and therefore safe for request forgery because it cannot redirect to an external server controlled by an attacker.
+
+.. code-block:: java
+
+ public static void barrierguard(URI uri) throws IOException {
+ if (!uri.isAbsolute()) { // The check guards the request, so the URI is safe.
+ URL url = uri.toURL();
+ url.openConnection(); // This is not a request forgery vulnerability.
+ }
+ }
+
+We need to add a tuple to the ``barrierGuardModel(package, type, subtypes, name, signature, ext, input, acceptingValue, kind, provenance)`` extensible predicate by updating a data extension file.
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/java-all
+ extensible: barrierGuardModel
+ data:
+ - ["java.net", "URI", True, "isAbsolute", "()", "", "Argument[this]", "false", "request-forgery", "manual"]
+
+The first five values identify the callable (in this case a method) to be modeled as a barrier guard.
+
+- The first value ``java.net`` is the package name.
+- The second value ``URI`` is the name of the class (type) that contains the method.
+- The third value ``True`` is a flag that indicates whether or not the model guard also applies to all overrides of the method.
+- The fourth value ``isAbsolute`` is the method name.
+- The fifth value ``()`` is the method input type signature.
+
+The sixth value should be left empty and is out of scope for this documentation.
+The remaining values are used to define the ``access-path``, the ``accepting-value``, the ``kind``, and the ``provenance`` (origin) of the barrier guard.
+
+- The seventh value ``Argument[this]`` is the access path to the input whose flow is blocked. In this case, the qualifier of the method call (``uri`` in the example).
+- The eighth value ``false`` is the accepting value of the barrier guard. This is the value that the conditional check must return for the barrier to apply. In this case, when ``isAbsolute`` is ``false``, the URI is relative and considered safe.
+- The ninth value ``request-forgery`` is the kind of the barrier guard. The barrier guard kind is used to define the queries where the barrier guard is in scope. In this case - the request forgery queries.
+- The tenth value ``manual`` is the provenance of the barrier guard, which is used to identify the origin of the barrier guard.
+
Example: Add a ``neutral`` method
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This example shows how the Java query pack models the ``now`` method as being neutral with respect to flow.
@@ -269,7 +345,7 @@ A neutral model is used to define that there is no flow through a method.
...
}
-We need to add a tuple to the ``neutralModel``\(package, type, name, signature, kind, provenance) extensible predicate by updating a data extension file.
+We need to add a tuple to the ``neutralModel(package, type, name, signature, kind, provenance)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -280,8 +356,6 @@ We need to add a tuple to the ``neutralModel``\(package, type, name, signature,
data:
- ["java.time", "Instant", "now", "()", "summary", "manual"]
-
-Since we are adding a neutral model, we need to add tuples to the ``neutralModel`` extensible predicate.
The first four values identify the callable (in this case a method) to be modeled as a neutral, the fifth value is the kind, and the sixth value is the provenance (origin) of the neutral.
- The first value ``java.time`` is the package name.
diff --git a/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst b/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
index b8f064c7574..a0702289cef 100644
--- a/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
+++ b/docs/codeql/codeql-language-guides/customizing-library-models-for-javascript.rst
@@ -22,24 +22,27 @@ A data extension for JavaScript is a YAML file of the form:
The CodeQL library for JavaScript exposes the following extensible predicates:
-- **sourceModel**\(type, path, kind)
-- **sinkModel**\(type, path, kind)
-- **typeModel**\(type1, type2, path)
-- **summaryModel**\(type, path, input, output, kind)
+- ``sourceModel(type, path, kind)``
+- ``sinkModel(type, path, kind)``
+- ``typeModel(type1, type2, path)``
+- ``summaryModel(type, path, input, output, kind)``
+- ``barrierModel(type, path, kind)``
+- ``barrierGuardModel(type, path, acceptingValue, kind)``
We'll explain how to use these using a few examples, and provide some reference material at the end of this article.
Example: Taint sink in the 'execa' package
------------------------------------------
-In this example, we'll show how to add the following argument, passed to **execa**, as a command-line injection sink:
+In this example, we'll show how to add the following argument, passed to ``execa``, as a command-line injection sink:
.. code-block:: js
import { shell } from "execa";
shell(cmd); // <-- add 'cmd' as a taint sink
-Note that this sink is already recognized by the CodeQL JS analysis, but for this example, you could use the following data extension:
+Note that this sink is already recognized by the CodeQL JS analysis, but for this example, you could add a tuple to the
+``sinkModel(type, path, kind)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -50,21 +53,19 @@ Note that this sink is already recognized by the CodeQL JS analysis, but for thi
data:
- ["execa", "Member[shell].Argument[0]", "command-injection"]
-
-- Since we're adding a new sink, we add a tuple to the **sinkModel** extensible predicate.
-- The first column, **"execa"**, identifies a set of values from which to begin the search for the sink.
- The string **"execa"** means we start at the places where the codebase imports the NPM package **execa**.
+- The first column, ``"execa"``, identifies a set of values from which to begin the search for the sink.
+ The string ``"execa"`` means we start at the places where the codebase imports the NPM package ``execa``.
- The second column is an access path that is evaluated from left to right, starting at the values that were identified by the first column.
- - **Member[shell]** selects accesses to the **shell** member of the **execa** package.
- - **Argument[0]** selects the first argument to calls to that member.
+ - ``Member[shell]`` selects accesses to the ``shell`` member of the ``execa`` package.
+ - ``Argument[0]`` selects the first argument to calls to that member.
-- **command-injection** indicates that this is considered a sink for the command injection query.
+- ``command-injection`` indicates that this is considered a sink for the command injection query.
Example: Taint sources from window 'message' events
---------------------------------------------------
-In this example, we'll show how the **event.data** expression below could be marked as a remote flow source:
+In this example, we'll show how the ``event.data`` expression below could be marked as a remote flow source:
.. code-block:: js
@@ -72,7 +73,8 @@ In this example, we'll show how the **event.data** expression below could be mar
let data = event.data; // <-- add 'event.data' as a taint source
});
-Note that this source is already known by the CodeQL JS analysis, but for this example, you could use the following data extension:
+Note that this source is already recognized by the CodeQL JS analysis, but for this example, you could add a tuple to the
+``sourceModel(type, path, kind)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -87,21 +89,19 @@ Note that this source is already known by the CodeQL JS analysis, but for this e
"remote",
]
-
-- Since we're adding a new taint source, we add a tuple to the **sourceModel** extensible predicate.
-- The first column, **"global"**, begins the search at references to the global object (also known as **window** in browser contexts). This is a special JavaScript object that contains all global variables and methods.
-- **Member[addEventListener]** selects accesses to the **addEventListener** member.
-- **Argument[1]** selects the second argument of calls to that member (the argument containing the callback).
-- **Parameter[0]** selects the first parameter of the callback (the parameter named **event**).
-- **Member[data]** selects accesses to the **data** property of the event object.
-- Finally, the kind **remote** indicates that this is considered a source of remote flow.
+- The first column, ``"global"``, begins the search at references to the global object (also known as ``window`` in browser contexts). This is a special JavaScript object that contains all global variables and methods.
+- ``Member[addEventListener]`` selects accesses to the ``addEventListener`` member.
+- ``Argument[1]`` selects the second argument of calls to that member (the argument containing the callback).
+- ``Parameter[0]`` selects the first parameter of the callback (the parameter named ``event``).
+- ``Member[data]`` selects accesses to the ``data`` property of the event object.
+- Finally, the kind ``remote`` indicates that this is considered a source of remote flow.
In the next section, we'll show how to restrict the model to recognize events of a specific type.
Continued example: Restricting the event type
---------------------------------------------
-The model above treats all events as sources of remote flow, not just **message** events.
+The model above treats all events as sources of remote flow, not just ``message`` events.
For example, it would also pick up this irrelevant source:
.. code-block:: js
@@ -111,7 +111,7 @@ For example, it would also pick up this irrelevant source:
});
-We can refine the model by adding the **WithStringArgument** component to restrict the set of calls being considered:
+We can refine the model by adding the ``WithStringArgument`` component to restrict the set of calls being considered:
.. code-block:: yaml
@@ -126,7 +126,7 @@ We can refine the model by adding the **WithStringArgument** component to restri
"remote",
]
-The **WithStringArgument[0=message]** component here selects the subset of calls to **addEventListener** where the first argument is a string literal with the value **"message"**.
+The ``WithStringArgument[0=message]`` component here selects the subset of calls to ``addEventListener`` where the first argument is a string literal with the value ``"message"``.
Example: Using types to add MySQL injection sinks
-------------------------------------------------
@@ -141,7 +141,7 @@ In this example, we'll show how to add the following SQL injection sink:
connection.query(q); // <-- add 'q' as a SQL injection sink
}
-We can recognize this using the following extension:
+We need to add a tuple to the ``sinkModel(type, path, kind)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -152,14 +152,13 @@ We can recognize this using the following extension:
data:
- ["mysql.Connection", "Member[query].Argument[0]", "sql-injection"]
+- The first column, ``"mysql.Connection"``, begins the search at any expression whose value is known to be an instance of
+ the ``Connection`` type from the ``mysql`` package. This will select the ``connection`` parameter above because of its type annotation.
+- ``Member[query]`` selects the ``query`` member from the connection object.
+- ``Argument[0]`` selects the first argument of a call to that member.
+- ``sql-injection`` indicates that this is considered a sink for the SQL injection query.
-- The first column, **"mysql.Connection"**, begins the search at any expression whose value is known to be an instance of
- the **Connection** type from the **mysql** package. This will select the **connection** parameter above because of its type annotation.
-- **Member[query]** selects the **query** member from the connection object.
-- **Argument[0]** selects the first argument of a call to that member.
-- **sql-injection** indicates that this is considered a sink for the SQL injection query.
-
-This works in this example because the **connection** parameter has a type annotation that matches what the model is looking for.
+This works in this example because the ``connection`` parameter has a type annotation that matches what the model is looking for.
Note that there is a significant difference between the following two rows:
@@ -169,8 +168,8 @@ Note that there is a significant difference between the following two rows:
- ["mysql.Connection", "", ...]
- ["mysql", "Member[Connection]", ...]
-The first row matches instances of **mysql.Connection**, which are objects that encapsulate a MySQL connection.
-The second row would match something like **require('mysql').Connection**, which is not itself a connection object.
+The first row matches instances of ``mysql.Connection``, which are objects that encapsulate a MySQL connection.
+The second row would match something like ``require('mysql').Connection``, which is not itself a connection object.
In the next section, we'll show how to generalize the model to handle the absence of type annotations.
@@ -185,8 +184,9 @@ Suppose we want the model from above to detect the sink in this snippet:
let connection = getConnection();
connection.query(q); // <-- add 'q' as a SQL injection sink
-There is no type annotation on **connection**, and there is no indication of what **getConnection()** returns.
-Using a **typeModel** tuple we can tell our model that this function returns an instance of **mysql.Connection**:
+There is no type annotation on ``connection``, and there is no indication of what ``getConnection()`` returns.
+By adding a tuple to the ``typeModel(type1, type2, path)`` extensible predicate we can tell our model that
+this function returns an instance of ``mysql.Connection``:
.. code-block:: yaml
@@ -197,19 +197,17 @@ Using a **typeModel** tuple we can tell our model that this function returns an
data:
- ["mysql.Connection", "@example/db", "Member[getConnection].ReturnValue"]
+- The first column, ``"mysql.Connection"``, names the type that we're adding a new definition for.
+- The second column, ``"@example/db"``, begins the search at imports of the hypothetical NPM package ``@example/db``.
+- ``Member[getConnection]`` selects references to the ``getConnection`` member from that package.
+- ``ReturnValue`` selects the return value from a call to that member.
-- Since we're providing type information, we add a tuple to the **typeModel** extensible predicate.
-- The first column, **"mysql.Connection"**, names the type that we're adding a new definition for.
-- The second column, **"@example/db"**, begins the search at imports of the hypothetical NPM package **@example/db**.
-- **Member[getConnection]** selects references to the **getConnection** member from that package.
-- **ReturnValue** selects the return value from a call to that member.
-
-The new model states that the return value of **getConnection()** has type **mysql.Connection**.
+The new model states that the return value of ``getConnection()`` has type ``mysql.Connection``.
Combining this with the sink model we added earlier, the sink in the example is detected by the model.
The mechanism used here is how library models work for both TypeScript and plain JavaScript.
-A good library model contains **typeModel** tuples to ensure it works even in codebases without type annotations.
-For example, the **mysql** model that is included with the CodeQL JS analysis includes this type definition (among many others):
+A good library model contains ``typeModel`` tuples to ensure it works even in codebases without type annotations.
+For example, the ``mysql`` model that is included with the CodeQL JS analysis includes this type definition (among many others):
.. code-block:: yaml
@@ -228,7 +226,7 @@ In this example, we'll show how to add the following SQL injection sink using a
conn.query(q, (err, rows) => {...}); // <-- add 'q' as a SQL injection sink
});
-We can recognize this using a fuzzy model, as shown in the following extension:
+We need to add a tuple for a fuzzy model to the ``sinkModel(type, path, kind)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -239,13 +237,13 @@ We can recognize this using a fuzzy model, as shown in the following extension:
data:
- ["mysql", "Fuzzy.Member[query].Argument[0]", "sql-injection"]
-- The first column, **"mysql"**, begins the search at places where the `mysql` package is imported.
-- **Fuzzy** selects all objects that appear to originate from the `mysql` package, such as the `pool`, `conn`, `err`, and `rows` objects.
-- **Member[query]** selects the **query** member from any of those objects. In this case, the only such member is `conn.query`.
+- The first column, ``"mysql"``, begins the search at places where the `mysql` package is imported.
+- ``Fuzzy`` selects all objects that appear to originate from the `mysql` package, such as the `pool`, `conn`, `err`, and `rows` objects.
+- ``Member[query]`` selects the ``query`` member from any of those objects. In this case, the only such member is `conn.query`.
In principle, this would also find expressions such as `pool.query` and `err.query`, but in practice such expressions
are not likely to occur, because the `pool` and `err` objects do not have a member named `query`.
-- **Argument[0]** selects the first argument of a call to the selected member, that is, the `q` argument to `conn.query`.
-- **sql-injection** indicates that this is considered as a sink for the SQL injection query.
+- ``Argument[0]`` selects the first argument of a call to the selected member, that is, the `q` argument to `conn.query`.
+- ``sql-injection`` indicates that this is considered as a sink for the SQL injection query.
For reference, a more detailed model might look like this, as described in the preceding examples:
@@ -265,7 +263,7 @@ For reference, a more detailed model might look like this, as described in the p
- ["mysql.Pool", "mysql", "Member[createPool].ReturnValue"]
- ["mysql.Connection", "mysql.Pool", "Member[getConnection].Argument[0].Parameter[1]"]
-The model using the **Fuzzy** component is simpler, at the cost of being approximate.
+The model using the ``Fuzzy`` component is simpler, at the cost of being approximate.
This technique is useful when modeling a large or complex library, where it is difficult to write a detailed model.
Example: Adding flow through 'decodeURIComponent'
@@ -277,7 +275,8 @@ In this example, we'll show how to add flow through calls to `decodeURIComponent
let y = decodeURIComponent(x); // add taint flow from 'x' to 'y'
-Note that this flow is already recognized by the CodeQL JS analysis, but for this example, you could use the following data extension:
+Note that this flow is already recognized by the CodeQL JS analysis, but for this example, you could add a tuple to the
+``summaryModel(type, path, input, output, kind)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -294,28 +293,27 @@ Note that this flow is already recognized by the CodeQL JS analysis, but for thi
"taint",
]
-
-- Since we're adding flow through a function call, we add a tuple to the **summaryModel** extensible predicate.
-- The first column, **"global"**, begins the search for relevant calls at references to the global object.
+- The first column, ``"global"``, begins the search for relevant calls at references to the global object.
In JavaScript, global variables are properties of the global object, so this lets us access global variables or functions.
-- The second column, **Member[decodeURIComponent]**, is a path leading to the function calls we wish to model.
- In this case, we select references to the **decodeURIComponent** member from the global object, that is,
- the global variable named **decodeURIComponent**.
-- The third column, **Argument[0]**, indicates the input of the flow. In this case, the first argument to the function call.
-- The fourth column, **ReturnValue**, indicates the output of the flow. In this case, the return value of the function call.
-- The last column, **taint**, indicates the kind of flow to add. The value **taint** means the output is not necessarily equal
+- The second column, ``Member[decodeURIComponent]``, is a path leading to the function calls we wish to model.
+ In this case, we select references to the ``decodeURIComponent`` member from the global object, that is,
+ the global variable named ``decodeURIComponent``.
+- The third column, ``Argument[0]``, indicates the input of the flow. In this case, the first argument to the function call.
+- The fourth column, ``ReturnValue``, indicates the output of the flow. In this case, the return value of the function call.
+- The last column, ``taint``, indicates the kind of flow to add. The value ``taint`` means the output is not necessarily equal
to the input, but was derived from the input in a taint-preserving way.
Example: Adding flow through 'underscore.forEach'
-------------------------------------------------
-In this example, we'll show how to add flow through calls to **forEach** from the **underscore** package:
+In this example, we'll show how to add flow through calls to ``forEach`` from the ``underscore`` package:
.. code-block:: js
require('underscore').forEach([x, y], (v) => { ... }); // add value flow from 'x' and 'y' to 'v'
-Note that this flow is already recognized by the CodeQL JS analysis, but for this example, you could use the following data extension:
+Note that this flow is already recognized by the CodeQL JS analysis, but for this example, you could add a tuple to the
+``summaryModel(type, path, input, output, kind)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -332,21 +330,19 @@ Note that this flow is already recognized by the CodeQL JS analysis, but for thi
"value",
]
-
-- Since we're adding flow through a function call, we add a tuple to the **summaryModel** extensible predicate.
-- The first column, **"underscore"**, begins the search for relevant calls at places where the **underscore** package is imported.
-- The second column, **Member[forEach]**, selects references to the **forEach** member from the **underscore** package.
+- The first column, ``"underscore"``, begins the search for relevant calls at places where the ``underscore`` package is imported.
+- The second column, ``Member[forEach]``, selects references to the ``forEach`` member from the ``underscore`` package.
- The third column specifies the input of the flow:
- - **Argument[0]** selects the first argument of **forEach**, which is the array being iterated over.
- - **ArrayElement** selects the elements of that array (the expressions **x** and **y**).
+ - ``Argument[0]`` selects the first argument of ``forEach``, which is the array being iterated over.
+ - ``ArrayElement`` selects the elements of that array (the expressions ``x`` and ``y``).
- The fourth column specifies the output of the flow:
- - **Argument[1]** selects the second argument of **forEach** (the argument containing the callback function).
- - **Parameter[0]** selects the first parameter of the callback function (the parameter named **v**).
+ - ``Argument[1]`` selects the second argument of ``forEach`` (the argument containing the callback function).
+ - ``Parameter[0]`` selects the first parameter of the callback function (the parameter named ``v``).
-- The last column, **value**, indicates the kind of flow to add. The value **value** means the input value is unchanged as
+- The last column, ``value``, indicates the kind of flow to add. The value ``value`` means the input value is unchanged as
it flows to the output.
@@ -367,7 +363,7 @@ on the incoming request objects:
req.data; // <-- mark 'req.data' as a taint source
});
-This can be achieved with the following data extension:
+We need to add a tuple to the ``sourceModel(type, path, kind)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -382,14 +378,66 @@ This can be achieved with the following data extension:
"remote",
]
-- Since we're adding a new taint source, we add a tuple to the **sourceModel** extensible predicate.
-- The first column, **"@example/middleware"**, begins the search at imports of the hypothetical NPM package **@example/middleware**.
-- **Member[injectData]** selects accesses to the **injectData** member.
-- **ReturnValue** selects the return value of the call to **injectData**.
-- **GuardedRouteHandler** interprets the current value as a middleware function and selects all route handlers guarded by that middleware. Since the current value is passd to **app.use()**, the callback subsequently passed to **app.get()** is seen as a guarded route handler.
-- **Parameter[0]** selects the first parameter of the callback (the parameter named **req**).
-- **Member[data]** selects accesses to the **data** property of the **req** object.
-- Finally, the kind **remote** indicates that this is considered a source of remote flow.
+- The first column, ``"@example/middleware"``, begins the search at imports of the hypothetical NPM package ``@example/middleware``.
+- ``Member[injectData]`` selects accesses to the ``injectData`` member.
+- ``ReturnValue`` selects the return value of the call to ``injectData``.
+- ``GuardedRouteHandler`` interprets the current value as a middleware function and selects all route handlers guarded by that middleware. Since the current value is passd to ``app.use()``, the callback subsequently passed to ``app.get()`` is seen as a guarded route handler.
+- ``Parameter[0]`` selects the first parameter of the callback (the parameter named ``req``).
+- ``Member[data]`` selects accesses to the ``data`` property of the ``req`` object.
+- Finally, the kind ``remote`` indicates that this is considered a source of remote flow.
+
+Example: Taint barrier using the 'encodeURIComponent' function
+--------------------------------------------------------------
+
+In this example, we'll show how to add the return value of ``encodeURIComponent`` as a barrier for XSS.
+
+.. code-block:: js
+
+ let escaped = encodeURIComponent(input); // The return value of this method is safe for XSS.
+ document.body.innerHTML = escaped;
+
+We need to add a tuple to the ``barrierModel(type, path, kind)`` extensible predicate by updating a data extension file.
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/javascript-all
+ extensible: barrierModel
+ data:
+ - ["global", "Member[encodeURIComponent].ReturnValue", "html-injection"]
+
+- The first column, ``"global"``, begins the search for relevant calls at references to the global object.
+- The second column, ``Member[encodeURIComponent].ReturnValue``, selects the return value of the ``encodeURIComponent`` function.
+- The third column, ``"html-injection"``, is the kind of the barrier.
+
+Example: Add a barrier guard
+----------------------------
+
+This example shows how to model a barrier guard that stops the flow of taint when a conditional check is performed on data.
+Consider a function called `isValid` which returns `true` when the data is considered safe.
+
+.. code-block:: js
+
+ if (isValid(userInput)) { // The check guards the use, so the input is safe.
+ db.query(userInput); // This is safe.
+ }
+
+We need to add a tuple to the ``barrierGuardModel(type, path, acceptingValue, kind)`` extensible predicate by updating a data extension file.
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/javascript-all
+ extensible: barrierGuardModel
+ data:
+ - ["my-package", "Member[isValid].Argument[0]", "true", "sql-injection"]
+
+- The first column, ``"my-package"``, begins the search at imports of the hypothetical NPM package ``my-package``.
+- The second column, ``Member[isValid].Argument[0]``, selects the first argument of the `isValid` function. This is the value being validated.
+- The third column, ``"true"``, is the accepting value of the barrier guard. This is the value that the conditional check must return for the barrier to apply.
+- The fourth column, ``"sql-injection"``, is the kind of the barrier guard.
Reference material
------------------
@@ -404,9 +452,9 @@ sourceModel(type, path, kind)
Adds a new taint source. Most taint-tracking queries will use the new source.
-- **type**: Name of a type from which to evaluate **path**.
-- **path**: Access path leading to the source.
-- **kind**: Kind of source to add. See the section on source kinds for a list of supported kinds.
+- ``type``: Name of a type from which to evaluate ``path``.
+- ``path``: Access path leading to the source.
+- ``kind``: Kind of source to add. See the section on source kinds for a list of supported kinds.
Example:
@@ -424,9 +472,9 @@ sinkModel(type, path, kind)
Adds a new taint sink. Sinks are query-specific and will typically affect one or two queries.
-- **type**: Name of a type from which to evaluate **path**.
-- **path**: Access path leading to the sink.
-- **kind**: Kind of sink to add. See the section on sink kinds for a list of supported kinds.
+- ``type``: Name of a type from which to evaluate ``path``.
+- ``path``: Access path leading to the sink.
+- ``kind``: Kind of sink to add. See the section on sink kinds for a list of supported kinds.
Example:
@@ -444,11 +492,11 @@ summaryModel(type, path, input, output, kind)
Adds flow through a function call.
-- **type**: Name of a type from which to evaluate **path**.
-- **path**: Access path leading to a function call.
-- **input**: Path relative to the function call that leads to input of the flow.
-- **output**: Path relative to the function call leading to the output of the flow.
-- **kind**: Kind of summary to add. Can be **taint** for taint-propagating flow, or **value** for value-preserving flow.
+- ``type``: Name of a type from which to evaluate ``path``.
+- ``path``: Access path leading to a function call.
+- ``input``: Path relative to the function call that leads to input of the flow.
+- ``output``: Path relative to the function call leading to the output of the flow.
+- ``kind``: Kind of summary to add. Can be ``taint`` for taint-propagating flow, or ``value`` for value-preserving flow.
Example:
@@ -472,9 +520,9 @@ typeModel(type1, type2, path)
Adds a new definition of a type.
-- **type1**: Name of the type to define.
-- **type2**: Name of the type from which to evaluate **path**.
-- **path**: Access path leading from **type2** to **type1**.
+- ``type1``: Name of the type to define.
+- ``type2``: Name of the type from which to evaluate ``path``.
+- ``path``: Access path leading from ``type2`` to ``type1``.
Example:
@@ -496,56 +544,56 @@ Types
A type is a string that identifies a set of values.
In each of the extensible predicates mentioned in previous section, the first column is always the name of a type.
-A type can be defined by adding **typeModel** tuples for that type. Additionally, the following built-in types are available:
+A type can be defined by adding ``typeModel`` tuples for that type. Additionally, the following built-in types are available:
-- The name of an NPM package matches imports of that package. For example, the type **express** matches the expression **require("express")**. If the package name includes dots, it must be surrounded by single quotes, such as in **'lodash.escape'**.
-- The type **global** identifies the global object, also known as **window**. In JavaScript, global variables are properties of the global object, so global variables can be identified using this type. (This type also matches imports of the NPM package named **global**, which is a package that happens to export the global object.)
-- A qualified type name of form **.** identifies expressions of type **** from ****. For example, **mysql.Connection** identifies expression of type **Connection** from the **mysql** package. Note that this only works if type annotations are present in the codebase, or if sufficient **typeModel** tuples have been provided for that type.
+- The name of an NPM package matches imports of that package. For example, the type ``express`` matches the expression ``require("express")``. If the package name includes dots, it must be surrounded by single quotes, such as in ``'lodash.escape'``.
+- The type ``global`` identifies the global object, also known as ``window``. In JavaScript, global variables are properties of the global object, so global variables can be identified using this type. (This type also matches imports of the NPM package named ``global``, which is a package that happens to export the global object.)
+- A qualified type name of form ``.`` identifies expressions of type ```` from ````. For example, ``mysql.Connection`` identifies expression of type ``Connection`` from the ``mysql`` package. Note that this only works if type annotations are present in the codebase, or if sufficient ``typeModel`` tuples have been provided for that type.
Access paths
------------
-The **path**, **input**, and **output** columns consist of a **.**-separated list of components, which is evaluated from left to right, with each step selecting a new set of values derived from the previous set of values.
+The ``path``, ``input``, and ``output`` columns consist of a ``.``-separated list of components, which is evaluated from left to right, with each step selecting a new set of values derived from the previous set of values.
The following components are supported:
-- **Argument[**\ `number`\ **]** selects the argument at the given index.
-- **Argument[this]** selects the receiver of a method call.
-- **Parameter[**\ `number`\ **]** selects the parameter at the given index.
-- **Parameter[this]** selects the **this** parameter of a function.
-- **ReturnValue** selects the return value of a function or call.
-- **Member[**\ `name`\ **]** selects the property with the given name.
-- **AnyMember** selects any property regardless of name.
-- **ArrayElement** selects an element of an array.
-- **MapValue** selects a value of a map object.
-- **Awaited** selects the value of a promise.
-- **Instance** selects instances of a class, including instances of its subclasses.
-- **Fuzzy** selects all values that are derived from the current value through a combination of the other operations described in this list.
+- ``Argument[``\ `number`\ ``]`` selects the argument at the given index.
+- ``Argument[this]`` selects the receiver of a method call.
+- ``Parameter[``\ `number`\ ``]`` selects the parameter at the given index.
+- ``Parameter[this]`` selects the ``this`` parameter of a function.
+- ``ReturnValue`` selects the return value of a function or call.
+- ``Member[``\ `name`\ ``]`` selects the property with the given name.
+- ``AnyMember`` selects any property regardless of name.
+- ``ArrayElement`` selects an element of an array.
+- ``MapValue`` selects a value of a map object.
+- ``Awaited`` selects the value of a promise.
+- ``Instance`` selects instances of a class, including instances of its subclasses.
+- ``Fuzzy`` selects all values that are derived from the current value through a combination of the other operations described in this list.
For example, this can be used to find all values that appear to originate from a particular package. This can be useful for finding method calls
from a known package, but where the receiver type is not known or is difficult to model.
The following components are called "call site filters". They select a subset of the previously-selected calls, if the call fits certain criteria:
-- **WithArity[**\ `number`\ **]** selects the subset of calls that have the given number of arguments.
-- **WithStringArgument[**\ `number`\ **=**\ `value`\ **]** selects the subset of calls where the argument at the given index is a string literal with the given value.
+- ``WithArity[``\ `number`\ ``]`` selects the subset of calls that have the given number of arguments.
+- ``WithStringArgument[``\ `number`\ ``=``\ `value`\ ``]`` selects the subset of calls where the argument at the given index is a string literal with the given value.
Components related to decorators:
-- **DecoratedClass** selects a class that has the current value as a decorator. For example, **Member[Component].DecoratedClass** selects any class that is decorated with **@Component**.
-- **DecoratedParameter** selects a parameter that is decorated by the current value.
-- **DecoratedMember** selects a method, field, or accessor that is decorated by the current value.
+- ``DecoratedClass`` selects a class that has the current value as a decorator. For example, ``Member[Component].DecoratedClass`` selects any class that is decorated with ``@Component``.
+- ``DecoratedParameter`` selects a parameter that is decorated by the current value.
+- ``DecoratedMember`` selects a method, field, or accessor that is decorated by the current value.
Additionally there is a component related to middleware functions:
-- **GuardedRouteHandler** interprets the current value as a middleware function, and selects any route handler function that comes after it in the routing hierarchy.
- This can be used to model properties injected onto request and response objects, such as **req.db** after a middleware that injects a database connection.
+- ``GuardedRouteHandler`` interprets the current value as a middleware function, and selects any route handler function that comes after it in the routing hierarchy.
+ This can be used to model properties injected onto request and response objects, such as ``req.db`` after a middleware that injects a database connection.
Note that this currently over-approximates the set of route handlers but may be made more accurate in the future.
Additional notes about the syntax of operands:
-- Multiple operands may be given to a single component, as a shorthand for the union of the operands. For example, **Member[foo,bar]** matches the union of **Member[foo]** and **Member[bar]**.
-- Numeric operands to **Argument**, **Parameter**, and **WithArity** may be given as an interval. For example, **Argument[0..2]** matches argument 0, 1, or 2.
-- **Argument[N-1]** selects the last argument of a call, and **Parameter[N-1]** selects the last parameter of a function, with **N-2** being the second-to-last and so on.
+- Multiple operands may be given to a single component, as a shorthand for the union of the operands. For example, ``Member[foo,bar]`` matches the union of ``Member[foo]`` and ``Member[bar]``.
+- Numeric operands to ``Argument``, ``Parameter``, and ``WithArity`` may be given as an interval. For example, ``Argument[0..2]`` matches argument 0, 1, or 2.
+- ``Argument[N-1]`` selects the last argument of a call, and ``Parameter[N-1]`` selects the last parameter of a function, with ``N-2`` being the second-to-last and so on.
Kinds
-----
@@ -553,14 +601,14 @@ Kinds
Source kinds
~~~~~~~~~~~~
-- **remote**: A general source of remote flow.
-- **browser**: A source in the browser environment that does not fit a more specific browser kind.
-- **browser-url-query**: A source derived from the query parameters of the browser URL, such as ``location.search``.
-- **browser-url-fragment**: A source derived from the fragment part of the browser URL, such as ``location.hash``.
-- **browser-url-path**: A source derived from the pathname of the browser URL, such as ``location.pathname``.
-- **browser-url**: A source derived from the browser URL, where the untrusted part is prefixed by trusted data such as the scheme and hostname.
-- **browser-window-name**: A source derived from the window name, such as ``window.name``.
-- **browser-message-event**: A source derived from cross-window message passing, such as ``event`` in ``window.onmessage = event => {...}``.
+- ``remote``: A general source of remote flow.
+- ``browser``: A source in the browser environment that does not fit a more specific browser kind.
+- ``browser-url-query``: A source derived from the query parameters of the browser URL, such as ``location.search``.
+- ``browser-url-fragment``: A source derived from the fragment part of the browser URL, such as ``location.hash``.
+- ``browser-url-path``: A source derived from the pathname of the browser URL, such as ``location.pathname``.
+- ``browser-url``: A source derived from the browser URL, where the untrusted part is prefixed by trusted data such as the scheme and hostname.
+- ``browser-window-name``: A source derived from the window name, such as ``window.name``.
+- ``browser-message-event``: A source derived from cross-window message passing, such as ``event`` in ``window.onmessage = event => {...}``.
See also :ref:`Threat models `.
@@ -569,22 +617,22 @@ Sink kinds
Unlike sources, sinks tend to be highly query-specific, rarely affecting more than one or two queries. Not every query supports customizable sinks. If the following sinks are not suitable for your use case, you should add a new query.
-- **code-injection**: A sink that can be used to inject code, such as in calls to **eval**.
-- **command-injection**: A sink that can be used to inject shell commands, such as in calls to **child_process.spawn**.
-- **path-injection**: A sink that can be used for path injection in a file system access, such as in calls to **fs.readFile**.
-- **sql-injection**: A sink that can be used for SQL injection, such as in a MySQL **query** call.
-- **nosql-injection**: A sink that can be used for NoSQL injection, such as in a MongoDB **findOne** call.
-- **html-injection**: A sink that can be used for HTML injection, such as in a jQuery **$()** call.
-- **request-forgery**: A sink that controls the URL of a request, such as in a **fetch** call.
-- **url-redirection**: A sink that can be used to redirect the user to a malicious URL.
-- **unsafe-deserialization**: A deserialization sink that can lead to code execution or other unsafe behaviour, such as an unsafe YAML parser.
-- **log-injection**: A sink that can be used for log injection, such as in a **console.log** call.
+- ``code-injection``: A sink that can be used to inject code, such as in calls to ``eval``.
+- ``command-injection``: A sink that can be used to inject shell commands, such as in calls to ``child_process.spawn``.
+- ``path-injection``: A sink that can be used for path injection in a file system access, such as in calls to ``fs.readFile``.
+- ``sql-injection``: A sink that can be used for SQL injection, such as in a MySQL ``query`` call.
+- ``nosql-injection``: A sink that can be used for NoSQL injection, such as in a MongoDB ``findOne`` call.
+- ``html-injection``: A sink that can be used for HTML injection, such as in a jQuery ``$()`` call.
+- ``request-forgery``: A sink that controls the URL of a request, such as in a ``fetch`` call.
+- ``url-redirection``: A sink that can be used to redirect the user to a malicious URL.
+- ``unsafe-deserialization``: A deserialization sink that can lead to code execution or other unsafe behaviour, such as an unsafe YAML parser.
+- ``log-injection``: A sink that can be used for log injection, such as in a ``console.log`` call.
Summary kinds
~~~~~~~~~~~~~
-- **taint**: A summary that propagates taint. This means the output is not necessarily equal to the input, but it was derived from the input in an unrestrictive way. An attacker who controls the input will have significant control over the output as well.
-- **value**: A summary that preserves the value of the input or creates a copy of the input such that all of its object properties are preserved.
+- ``taint``: A summary that propagates taint. This means the output is not necessarily equal to the input, but it was derived from the input in an unrestrictive way. An attacker who controls the input will have significant control over the output as well.
+- ``value``: A summary that preserves the value of the input or creates a copy of the input such that all of its object properties are preserved.
.. _threat-models-javascript:
diff --git a/docs/codeql/codeql-language-guides/customizing-library-models-for-python.rst b/docs/codeql/codeql-language-guides/customizing-library-models-for-python.rst
index 30888f7b609..ee4565caff3 100644
--- a/docs/codeql/codeql-language-guides/customizing-library-models-for-python.rst
+++ b/docs/codeql/codeql-language-guides/customizing-library-models-for-python.rst
@@ -9,7 +9,7 @@ Python analysis can be customized by adding library models in data extension fil
A data extension for Python is a YAML file of the form:
-.. code-block:: yaml
+.. code-block:: yaml
extensions:
- addsTo:
@@ -22,24 +22,27 @@ A data extension for Python is a YAML file of the form:
The CodeQL library for Python exposes the following extensible predicates:
-- **sourceModel**\(type, path, kind)
-- **sinkModel**\(type, path, kind)
-- **typeModel**\(type1, type2, path)
-- **summaryModel**\(type, path, input, output, kind)
+- ``sourceModel(type, path, kind)``
+- ``sinkModel(type, path, kind)``
+- ``typeModel(type1, type2, path)``
+- ``summaryModel(type, path, input, output, kind)``
+- ``barrierModel(type, path, kind)``
+- ``barrierGuardModel(type, path, acceptingValue, kind)``
We'll explain how to use these using a few examples, and provide some reference material at the end of this article.
Example: Taint sink in the 'fabric' package
-------------------------------------------
-In this example, we'll show how to add the following argument, passed to **sudo** from the **fabric** package, as a command-line injection sink:
+In this example, we'll show how to add the following argument, passed to ``sudo`` from the ``fabric`` package, as a command-line injection sink:
.. code-block:: python
from fabric.operations import sudo
sudo(cmd) # <-- add 'cmd' as a taint sink
-Note that this sink is already recognized by the CodeQL Python analysis, but for this example, you could use the following data extension:
+Note that this sink is already recognized by the CodeQL Python analysis, but for this example, you could add a tuple to the
+``sinkModel(type, path, kind)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -50,22 +53,20 @@ Note that this sink is already recognized by the CodeQL Python analysis, but for
data:
- ["fabric", "Member[operations].Member[sudo].Argument[0]", "command-injection"]
-
-- Since we're adding a new sink, we add a tuple to the **sinkModel** extensible predicate.
-- The first column, **"fabric"**, identifies a set of values from which to begin the search for the sink.
- The string **"fabric"** means we start at the places where the codebase imports the package **fabric**.
+- The first column, ``"fabric"``, identifies a set of values from which to begin the search for the sink.
+ The string ``"fabric"`` means we start at the places where the codebase imports the package ``fabric``.
- The second column is an access path that is evaluated from left to right, starting at the values that were identified by the first column.
- - **Member[operations]** selects accesses to the **operations** module.
- - **Member[sudo]** selects accesses to the **sudo** function in the **operations** module.
- - **Argument[0]** selects the first argument to calls to that function.
+ - ``Member[operations]`` selects accesses to the ``operations`` module.
+ - ``Member[sudo]`` selects accesses to the ``sudo`` function in the ``operations`` module.
+ - ``Argument[0]`` selects the first argument to calls to that function.
-- **"command-injection"** indicates that this is considered a sink for the command injection query.
+- ``"command-injection"`` indicates that this is considered a sink for the command injection query.
Example: Taint sink in the 'invoke' package
-------------------------------------------
-Often sinks are found as arguments to methods rather than functions. In this example, we'll show how to add the following argument, passed to **run** from the **invoke** package, as a command-line injection sink:
+Often sinks are found as arguments to methods rather than functions. In this example, we'll show how to add the following argument, passed to ``run`` from the ``invoke`` package, as a command-line injection sink:
.. code-block:: python
@@ -73,7 +74,8 @@ Often sinks are found as arguments to methods rather than functions. In this exa
c = invoke.Context()
c.run(cmd) # <-- add 'cmd' as a taint sink
-Note that this sink is already recognized by the CodeQL Python analysis, but for this example, you could use the following data extension:
+Note that this sink is already recognized by the CodeQL Python analysis, but for this example, you could add a tuple to the
+``sinkModel(type, path, kind)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -84,17 +86,17 @@ Note that this sink is already recognized by the CodeQL Python analysis, but for
data:
- ["invoke", "Member[Context].Instance.Member[run].Argument[0]", "command-injection"]
-- The first column, **"invoke"**, begins the search at places where the codebase imports the package **invoke**.
+- The first column, ``"invoke"``, begins the search at places where the codebase imports the package ``invoke``.
- The second column is an access path that is evaluated from left to right, starting at the values that were identified by the first column.
- - **Member[Context]** selects accesses to the **Context** class.
- - **Instance** selects instances of the **Context** class.
- - **Member[run]** selects accesses to the **run** method in the **Context** class.
- - **Argument[0]** selects the first argument to calls to that method.
+ - ``Member[Context]`` selects accesses to the ``Context`` class.
+ - ``Instance`` selects instances of the ``Context`` class.
+ - ``Member[run]`` selects accesses to the ``run`` method in the ``Context`` class.
+ - ``Argument[0]`` selects the first argument to calls to that method.
-- **"command-injection"** indicates that this is considered a sink for the command injection query.
+- ``"command-injection"`` indicates that this is considered a sink for the command injection query.
-Note that the **Instance** component is used to select instances of a class, including instances of its subclasses.
+Note that the ``Instance`` component is used to select instances of a class, including instances of its subclasses.
Since methods on instances are common targets, we have a more compact syntax for selecting them. The first column, the type, is allowed to contain a dotted path ending in a class name.
This will begin the search at instances of that class. Using this syntax, the previous example could be written as:
@@ -110,7 +112,7 @@ This will begin the search at instances of that class. Using this syntax, the pr
Continued example: Multiple ways to obtain a type
-------------------------------------------------
-The invoke package provides multiple ways to obtain a **Context** instance. The following example shows how to add a new way to obtain a **Context** instance:
+The invoke package provides multiple ways to obtain a ``Context`` instance. The following example shows how to add a new way to obtain a ``Context`` instance:
.. code-block:: python
@@ -118,8 +120,9 @@ The invoke package provides multiple ways to obtain a **Context** instance. The
c = context.Context()
c.run(cmd) # <-- add 'cmd' as a taint sink
-Comparing to the previous Python snippet, the **Context** class is now found as **invoke.context.Context** instead of **invoke.Context**.
-We could add a data extension similar to the previous one, but with the type **invoke.context.Context**. However, we can also use the **typeModel** extensible predicate to describe how to reach **invoke.Context** from **invoke.context.Context**:
+Comparing to the previous Python snippet, the ``Context`` class is now found as ``invoke.context.Context`` instead of ``invoke.Context``.
+We could add a data extension similar to the previous one, but with the type ``invoke.context.Context``.
+However, we can also use the ``typeModel(type1, type2, path)`` extensible predicate to describe how to reach ``invoke.Context`` from ``invoke.context.Context``:
.. code-block:: yaml
@@ -130,9 +133,9 @@ We could add a data extension similar to the previous one, but with the type **i
data:
- ["invoke.Context", "invoke.context.Context", ""]
-- The first column, **"invoke.Context"**, is the name of the type to reach.
-- The second column, **"invoke.context.Context"**, is the name of the type from which to evaluate the path.
-- The third column is just an empty string, indicating that any instance of **invoke.context.Context** is also an instance of **invoke.Context**.
+- The first column, ``"invoke.Context"``, is the name of the type to reach.
+- The second column, ``"invoke.context.Context"``, is the name of the type from which to evaluate the path.
+- The third column is just an empty string, indicating that any instance of ``invoke.context.Context`` is also an instance of ``invoke.Context``.
Combining this with the sink model we added earlier, the sink in the example is detected by the model.
@@ -141,7 +144,7 @@ Example: Taint sources from Django 'upload_to' argument
This example is a bit more advanced, involving both a callback function and a class constructor.
The Django web framework allows you to specify a function that determines the path where uploaded files are stored (see the `Django documentation `_).
-This function is passed as an argument to the **FileField** constructor.
+This function is passed as an argument to the ``FileField`` constructor.
The function is called with two arguments: the instance of the model and the filename of the uploaded file.
This filename is what we want to mark as a taint source. An example use looks as follows:
@@ -156,7 +159,8 @@ This filename is what we want to mark as a taint source. An example use looks as
class MyModel(models.Model):
upload = models.FileField(upload_to=user_directory_path) # <-- the 'upload_to' parameter defines our custom function
-Note that this source is already known by the CodeQL Python analysis, but for this example, you could use the following data extension:
+Note that this source is already recognized by the CodeQL Python analysis, but for this example, you could add a tuple to the
+``sourceModel(type, path, kind)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -171,18 +175,16 @@ Note that this source is already known by the CodeQL Python analysis, but for th
"remote",
]
-
-- Since we're adding a new taint source, we add a tuple to the **sourceModel** extensible predicate.
-- The first column, **"django.db.models.FileField!"**, is a dotted path to the **FileField** class from the **django.db.models** package.
- The **!** at the end of the type name indicates that we are looking for the class itself rather than instances of this class.
+- The first column, ``"django.db.models.FileField!"``, is a dotted path to the ``FileField`` class from the ``django.db.models`` package.
+ The ``!`` at the end of the type name indicates that we are looking for the class itself rather than instances of this class.
- The second column is an access path that is evaluated from left to right, starting at the values that were identified by the first column.
-
- - **Call** selects calls to the class. That is, constructor calls.
- - **Argument[0,upload_to:]** selects the first positional argument, or the named argument named **upload_to**. Note that the colon at the end of the argument name indicates that we are looking for a named argument.
- - **Parameter[1]** selects the second parameter of the callback function, which is the parameter receiving the filename.
-- Finally, the kind **"remote"** indicates that this is considered a source of remote flow.
+ - ``Call`` selects calls to the class. That is, constructor calls.
+ - ``Argument[0,upload_to:]`` selects the first positional argument, or the named argument named ``upload_to``. Note that the colon at the end of the argument name indicates that we are looking for a named argument.
+ - ``Parameter[1]`` selects the second parameter of the callback function, which is the parameter receiving the filename.
+
+- Finally, the kind ``"remote"`` indicates that this is considered a source of remote flow.
Example: Adding flow through 're.compile'
----------------------------------------------
@@ -196,7 +198,8 @@ In this example, we'll show how to add flow through calls to ``re.compile``.
let y = re.compile(pattern = x); // add value flow from 'x' to 'y.pattern'
-Note that this flow is already recognized by the CodeQL Python analysis, but for this example, you could use the following data extension:
+Note that this flow is already recognized by the CodeQL Python analysis, but for this example, you could add a tuple to the
+``summaryModel(type, path, input, output, kind)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -213,26 +216,25 @@ Note that this flow is already recognized by the CodeQL Python analysis, but for
"value",
]
-
-- Since we're adding flow through a function call, we add a tuple to the **summaryModel** extensible predicate.
-- The first column, **"re"**, begins the search for relevant calls at places where the **re** package is imported.
-- The second column, **"Member[compile]"**, is a path leading to the function calls we wish to model.
- In this case, we select references to the **compile** function from the ``re`` package.
-- The third column, **"Argument[0,pattern:]"**, indicates the input of the flow. In this case, either the first argument to the function call or the argument named **pattern**.
-- The fourth column, **"ReturnValue.Attribute[pattern]"**, indicates the output of the flow. In this case, the ``pattern`` attribute of the return value of the function call.
-- The last column, **"value"**, indicates the kind of flow to add. The value **value** means the input value is unchanged as
+- The first column, ``"re"``, begins the search for relevant calls at places where the ``re`` package is imported.
+- The second column, ``"Member[compile]"``, is a path leading to the function calls we wish to model.
+ In this case, we select references to the ``compile`` function from the ``re`` package.
+- The third column, ``"Argument[0,pattern:]"``, indicates the input of the flow. In this case, either the first argument to the function call or the argument named ``pattern``.
+- The fourth column, ``"ReturnValue.Attribute[pattern]"``, indicates the output of the flow. In this case, the ``pattern`` attribute of the return value of the function call.
+- The last column, ``"value"``, indicates the kind of flow to add. The value ``value`` means the input value is unchanged as
it flows to the output.
Example: Adding flow through 'sorted'
-------------------------------------------------
-In this example, we'll show how to add flow through calls to the built-in function **sorted**:
+In this example, we'll show how to add flow through calls to the built-in function ``sorted``:
.. code-block:: python
y = sorted(x) # add taint flow from 'x' to 'y'
-Note that this flow is already recognized by the CodeQL Python analysis, but for this example, you could use the following data extension:
+Note that this flow is already recognized by the CodeQL Python analysis, but for this example, you could add a tuple to the
+``summaryModel(type, path, input, output, kind)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -249,14 +251,12 @@ Note that this flow is already recognized by the CodeQL Python analysis, but for
"taint",
]
-
-- Since we're adding flow through a function call, we add a tuple to the **summaryModel** extensible predicate.
-- The first column, **"builtins"**, begins the search for relevant calls among references to the built-in names.
- In Python, many built-in functions are available. Technically, most of these are part of the **builtins** package, but they can be accessed without an explicit import. When we write **builtins** in the first column, we will find both the implicit and explicit references to the built-in functions.
-- The second column, **"Member[sorted]"**, selects references to the **sorted** function from the **builtins** package; that is, the built-in function **sorted**.
-- The third column, **"Argument[0]"**, indicates the input of the flow. In this case, the first argument to the function call.
-- The fourth column, **"ReturnValue"**, indicates the output of the flow. In this case, the return value of the function call.
-- The last column, **"taint"**, indicates the kind of flow to add. The value **taint** means the output is not necessarily equal
+- The first column, ``"builtins"``, begins the search for relevant calls among references to the built-in names.
+ In Python, many built-in functions are available. Technically, most of these are part of the ``builtins`` package, but they can be accessed without an explicit import. When we write ``builtins`` in the first column, we will find both the implicit and explicit references to the built-in functions.
+- The second column, ``"Member[sorted]"``, selects references to the ``sorted`` function from the ``builtins`` package; that is, the built-in function ``sorted``.
+- The third column, ``"Argument[0]"``, indicates the input of the flow. In this case, the first argument to the function call.
+- The fourth column, ``"ReturnValue"``, indicates the output of the flow. In this case, the return value of the function call.
+- The last column, ``"taint"``, indicates the kind of flow to add. The value ``taint`` means the output is not necessarily equal
to the input, but was derived from the input in a taint-preserving way.
We might also provide a summary stating that the elements of the input list are preserved in the output list:
@@ -279,6 +279,64 @@ We might also provide a summary stating that the elements of the input list are
The tracking of list elements is imprecise in that the analysis does not know where in the list the tracked value is found.
So this summary simply states that if the value is found somewhere in the input list, it will also be found somewhere in the output list, unchanged.
+Example: Taint barrier using the 'escape' function
+--------------------------------------------------
+
+In this example, we'll show how to add the return value of ``html.escape`` as a barrier for XSS.
+
+.. code-block:: python
+
+ import html
+ escaped = html.escape(unknown) # The return value of this function is safe for XSS.
+
+We need to add a tuple to the ``barrierModel(type, path, kind)`` extensible predicate by updating a data extension file.
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/python-all
+ extensible: barrierModel
+ data:
+ - ["html", "Member[escape].ReturnValue", "html-injection"]
+
+- The first column, ``"html"``, begins the search at places where the ``html`` module is imported.
+- The second column, ``Member[escape].ReturnValue``, selects the return value of the ``escape`` function from the ``html`` module.
+- The third column, ``"html-injection"``, is the kind of the barrier.
+
+Example: Add a barrier guard
+----------------------------
+
+This example shows how to model a barrier guard that stops the flow of taint when a conditional check is performed on data.
+A barrier guard model is used when a function returns a boolean that indicates whether the data is safe to use.
+Consider the function ``url_has_allowed_host_and_scheme`` from the ``django.utils.http`` package which returns ``true`` when the URL is in a safe domain.
+
+.. code-block:: python
+
+ if url_has_allowed_host_and_scheme(url, allowed_hosts=...): # The check guards the use of 'url', so it is safe.
+ redirect(url) # This is safe.
+
+We need to add a tuple to the ``barrierGuardModel(type, path, acceptingValue, kind)`` extensible predicate by updating a data extension file.
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/python-all
+ extensible: barrierGuardModel
+ data:
+ - [
+ "django",
+ "Member[utils].Member[http].Member[url_has_allowed_host_and_scheme].Argument[0,url:]",
+ "true",
+ "url-redirection",
+ ]
+
+- The first column, ``"django"``, begins the search at places where the ``django`` package is imported.
+- The second column, ``Member[utils].Member[http].Member[url_has_allowed_host_and_scheme].Argument[0,url:]``, selects the first argument (or the keyword argument ``url``) of the ``url_has_allowed_host_and_scheme`` function in the ``django.utils.http`` module. This is the value being validated.
+- The third column, ``"true"``, is the accepting value of the barrier guard. This is the value that the conditional check must return for the barrier to apply.
+- The fourth column, ``"url-redirection"``, is the kind of the barrier guard. The barrier guard kind is used to define the queries where the barrier guard is in scope.
+
Reference material
------------------
@@ -292,9 +350,9 @@ sourceModel(type, path, kind)
Adds a new taint source. Most taint-tracking queries will use the new source.
-- **type**: Name of a type from which to evaluate **path**.
-- **path**: Access path leading to the source.
-- **kind**: Kind of source to add. Currently only **remote** is used.
+- ``type``: Name of a type from which to evaluate ``path``.
+- ``path``: Access path leading to the source.
+- ``kind``: Kind of source to add. Currently only ``remote`` is used.
Example:
@@ -312,9 +370,9 @@ sinkModel(type, path, kind)
Adds a new taint sink. Sinks are query-specific and will typically affect one or two queries.
-- **type**: Name of a type from which to evaluate **path**.
-- **path**: Access path leading to the sink.
-- **kind**: Kind of sink to add. See the section on sink kinds for a list of supported kinds.
+- ``type``: Name of a type from which to evaluate ``path``.
+- ``path``: Access path leading to the sink.
+- ``kind``: Kind of sink to add. See the section on sink kinds for a list of supported kinds.
Example:
@@ -332,11 +390,11 @@ summaryModel(type, path, input, output, kind)
Adds flow through a function call.
-- **type**: Name of a type from which to evaluate **path**.
-- **path**: Access path leading to a function call.
-- **input**: Path relative to the function call that leads to input of the flow.
-- **output**: Path relative to the function call leading to the output of the flow.
-- **kind**: Kind of summary to add. Can be **taint** for taint-propagating flow, or **value** for value-preserving flow.
+- ``type``: Name of a type from which to evaluate ``path``.
+- ``path``: Access path leading to a function call.
+- ``input``: Path relative to the function call that leads to input of the flow.
+- ``output``: Path relative to the function call leading to the output of the flow.
+- ``kind``: Kind of summary to add. Can be ``taint`` for taint-propagating flow, or ``value`` for value-preserving flow.
Example:
@@ -358,13 +416,13 @@ Example:
typeModel(type1, type2, path)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-A description of how to reach **type1** from **type2**.
-If this is the only way to reach **type1**, for instance if **type1** is a name we made up to represent the inner workings of a library, we think of this as a definition of **type1**.
-In the context of instances, this describes how to obtain an instance of **type1** from an instance of **type2**.
+A description of how to reach ``type1`` from ``type2``.
+If this is the only way to reach ``type1``, for instance if ``type1`` is a name we made up to represent the inner workings of a library, we think of this as a definition of ``type1``.
+In the context of instances, this describes how to obtain an instance of ``type1`` from an instance of ``type2``.
-- **type1**: Name of the type to reach.
-- **type2**: Name of the type from which to evaluate **path**.
-- **path**: Access path leading from **type2** to **type1**.
+- ``type1``: Name of the type to reach.
+- ``type2``: Name of the type from which to evaluate ``path``.
+- ``path``: Access path leading from ``type2`` to ``type1``.
Example:
@@ -386,40 +444,40 @@ Types
A type is a string that identifies a set of values.
In each of the extensible predicates mentioned in previous section, the first column is always the name of a type.
-A type can be defined by adding **typeModel** tuples for that type. Additionally, the following built-in types are available:
+A type can be defined by adding ``typeModel`` tuples for that type. Additionally, the following built-in types are available:
-- The name of a package matches imports of that package. For example, the type **django** matches the expression **import django**.
-- The type **builtins** identifies the builtins package. In Python, all built-in values are found in this package, so they can be identified using this type.
-- A dotted path ending in a class name identifies instances of that class. If the suffix **!** is added, the type refers to the class itself.
+- The name of a package matches imports of that package. For example, the type ``django`` matches the expression ``import django``.
+- The type ``builtins`` identifies the builtins package. In Python, all built-in values are found in this package, so they can be identified using this type.
+- A dotted path ending in a class name identifies instances of that class. If the suffix ``!`` is added, the type refers to the class itself.
Access paths
------------
-The **path**, **input**, and **output** columns consist of a **.**-separated list of components, which is evaluated from left to right, with each step selecting a new set of values derived from the previous set of values.
+The ``path``, ``input``, and ``output`` columns consist of a ``.``-separated list of components, which is evaluated from left to right, with each step selecting a new set of values derived from the previous set of values.
The following components are supported:
-- **Argument[**\ ``number``\ **]** selects the argument at the given index.
-- **Argument[**\ ``name``:\ **]** selects the argument with the given name.
-- **Argument[this]** selects the receiver of a method call.
-- **Parameter[**\ ``number``\ **]** selects the parameter at the given index.
-- **Parameter[**\ ``name``:\ **]** selects the named parameter with the given name.
-- **Parameter[this]** selects the **this** parameter of a function.
-- **ReturnValue** selects the return value of a function or call.
-- **Member[**\ ``name``\ **]** selects the function/method/class/value with the given name.
-- **Instance** selects instances of a class, including instances of its subclasses.
-- **Attribute[**\ ``name``\ **]** selects the attribute with the given name.
-- **ListElement** selects an element of a list.
-- **SetElement** selects an element of a set.
-- **TupleElement[**\ ``number``\ **]** selects the subscript at the given index.
-- **DictionaryElement[**\ ``name``\ **]** selects the subscript at the given name.
+- ``Argument[``\ ``number``\ ``]`` selects the argument at the given index.
+- ``Argument[``\ ``name``:\ ``]`` selects the argument with the given name.
+- ``Argument[this]`` selects the receiver of a method call.
+- ``Parameter[``\ ``number``\ ``]`` selects the parameter at the given index.
+- ``Parameter[``\ ``name``:\ ``]`` selects the named parameter with the given name.
+- ``Parameter[this]`` selects the ``this`` parameter of a function.
+- ``ReturnValue`` selects the return value of a function or call.
+- ``Member[``\ ``name``\ ``]`` selects the function/method/class/value with the given name.
+- ``Instance`` selects instances of a class, including instances of its subclasses.
+- ``Attribute[``\ ``name``\ ``]`` selects the attribute with the given name.
+- ``ListElement`` selects an element of a list.
+- ``SetElement`` selects an element of a set.
+- ``TupleElement[``\ ``number``\ ``]`` selects the subscript at the given index.
+- ``DictionaryElement[``\ ``name``\ ``]`` selects the subscript at the given name.
Additional notes about the syntax of operands:
-- Multiple operands may be given to a single component, as a shorthand for the union of the operands. For example, **Member[foo,bar]** matches the union of **Member[foo]** and **Member[bar]**.
-- Numeric operands to **Argument**, **Parameter**, and **WithArity** may be given as an interval. For example, **Argument[0..2]** matches argument 0, 1, or 2.
-- **Argument[N-1]** selects the last argument of a call, and **Parameter[N-1]** selects the last parameter of a function, with **N-2** being the second-to-last and so on.
+- Multiple operands may be given to a single component, as a shorthand for the union of the operands. For example, ``Member[foo,bar]`` matches the union of ``Member[foo]`` and ``Member[bar]``.
+- Numeric operands to ``Argument``, ``Parameter``, and ``WithArity`` may be given as an interval. For example, ``Argument[0..2]`` matches argument 0, 1, or 2.
+- ``Argument[N-1]`` selects the last argument of a call, and ``Parameter[N-1]`` selects the last parameter of a function, with ``N-2`` being the second-to-last and so on.
Kinds
-----
@@ -434,21 +492,21 @@ Sink kinds
Unlike sources, sinks tend to be highly query-specific, rarely affecting more than one or two queries. Not every query supports customizable sinks. If the following sinks are not suitable for your use case, you should add a new query.
-- **code-injection**: A sink that can be used to inject code, such as in calls to **exec**.
-- **command-injection**: A sink that can be used to inject shell commands, such as in calls to **os.system**.
-- **path-injection**: A sink that can be used for path injection in a file system access, such as in calls to **flask.send_from_directory**.
-- **sql-injection**: A sink that can be used for SQL injection, such as in a MySQL **query** call.
-- **html-injection**: A sink that can be used for HTML injection, such as a server response body.
-- **js-injection**: A sink that can be used for JS injection, such as a server response body.
-- **url-redirection**: A sink that can be used to redirect the user to a malicious URL.
-- **unsafe-deserialization**: A deserialization sink that can lead to code execution or other unsafe behavior, such as an unsafe YAML parser.
-- **log-injection**: A sink that can be used for log injection, such as in a **logging.info** call.
+- ``code-injection``: A sink that can be used to inject code, such as in calls to ``exec``.
+- ``command-injection``: A sink that can be used to inject shell commands, such as in calls to ``os.system``.
+- ``path-injection``: A sink that can be used for path injection in a file system access, such as in calls to ``flask.send_from_directory``.
+- ``sql-injection``: A sink that can be used for SQL injection, such as in a MySQL ``query`` call.
+- ``html-injection``: A sink that can be used for HTML injection, such as a server response body.
+- ``js-injection``: A sink that can be used for JS injection, such as a server response body.
+- ``url-redirection``: A sink that can be used to redirect the user to a malicious URL.
+- ``unsafe-deserialization``: A deserialization sink that can lead to code execution or other unsafe behavior, such as an unsafe YAML parser.
+- ``log-injection``: A sink that can be used for log injection, such as in a ``logging.info`` call.
Summary kinds
~~~~~~~~~~~~~
-- **taint**: A summary that propagates taint. This means the output is not necessarily equal to the input, but it was derived from the input in an unrestrictive way. An attacker who controls the input will have significant control over the output as well.
-- **value**: A summary that preserves the value of the input or creates a copy of the input such that all of its object properties are preserved.
+- ``taint``: A summary that propagates taint. This means the output is not necessarily equal to the input, but it was derived from the input in an unrestrictive way. An attacker who controls the input will have significant control over the output as well.
+- ``value``: A summary that preserves the value of the input or creates a copy of the input such that all of its object properties are preserved.
.. _threat-models-python:
diff --git a/docs/codeql/codeql-language-guides/customizing-library-models-for-ruby.rst b/docs/codeql/codeql-language-guides/customizing-library-models-for-ruby.rst
index 23a6bd419f5..db041a52151 100644
--- a/docs/codeql/codeql-language-guides/customizing-library-models-for-ruby.rst
+++ b/docs/codeql/codeql-language-guides/customizing-library-models-for-ruby.rst
@@ -23,24 +23,27 @@ A data extension for Ruby is a YAML file of the form:
The CodeQL library for Ruby exposes the following extensible predicates:
-- **sourceModel**\(type, path, kind)
-- **sinkModel**\(type, path, kind)
-- **typeModel**\(type1, type2, path)
-- **summaryModel**\(type, path, input, output, kind)
+- ``sourceModel(type, path, kind)``
+- ``sinkModel(type, path, kind)``
+- ``typeModel(type1, type2, path)``
+- ``summaryModel(type, path, input, output, kind)``
+- ``barrierModel(type, path, kind)``
+- ``barrierGuardModel(type, path, acceptingValue, kind)``
We'll explain how to use these using a few examples, and provide some reference material at the end of this article.
Example: Taint sink in the 'tty-command' gem
--------------------------------------------
-In this example, we'll show how to add the following argument, passed to **tty-command**, as a command-line injection sink:
+In this example, we'll show how to add the following argument, passed to ``tty-command``, as a command-line injection sink:
.. code-block:: ruby
tty = TTY::Command.new
tty.run(cmd) # <-- add 'cmd' as a taint sink
-For this example, you can use the following data extension:
+
+We need to add a tuple to the ``sinkModel(type, path, kind)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -52,15 +55,14 @@ For this example, you can use the following data extension:
- ["TTY::Command", "Method[run].Argument[0]", "command-injection"]
-- Since we're adding a new sink, we add a tuple to the **sinkModel** extensible predicate.
-- The first column, **"TTY::Command"**, identifies a set of values from which to begin the search for the sink.
- The string **"TTY::Command""** means we start at the places where the codebase constructs instances of the class **TTY::Command**.
+- The first column, ``"TTY::Command"``, identifies a set of values from which to begin the search for the sink.
+ The string ``"TTY::Command""`` means we start at the places where the codebase constructs instances of the class ``TTY::Command``.
- The second column is an access path that is evaluated from left to right, starting at the values that were identified by the first column.
- - **Method[run]** selects calls to the **run** method of the **TTY::Command** class.
- - **Argument[0]** selects the first argument to calls to that member.
+ - ``Method[run]`` selects calls to the ``run`` method of the ``TTY::Command`` class.
+ - ``Argument[0]`` selects the first argument to calls to that member.
-- **command-injection** indicates that this is considered a sink for the command injection query.
+- ``command-injection`` indicates that this is considered a sink for the command injection query.
Example: Taint sources from 'sinatra' block parameters
------------------------------------------------------
@@ -75,7 +77,7 @@ In this example, we'll show how the 'x' parameter below could be marked as a rem
end
end
-For this example you could use the following data extension:
+We need to add a tuple to the ``sourceModel(type, path, kind)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -90,13 +92,12 @@ For this example you could use the following data extension:
"remote",
]
-- Since we're adding a new taint source, we add a tuple to the **sourceModel** extensible predicate.
-- The first column, **"Sinatra::Base!"**, begins the search at references to the **Sinatra::Base** class.
- The **!** suffix indicates that we want to search for references to the class itself, rather than instances of the class.
-- **Method[get]** selects calls to the **get** method of the **Sinatra::Base** class.
-- **Argument[block]** selects the block argument to the **get** method call.
-- **Parameter[0]** selects the first parameter of the block argument (the parameter named **x**).
-- Finally, the kind **remote** indicates that this is considered a source of remote flow.
+- The first column, ``"Sinatra::Base!"``, begins the search at references to the ``Sinatra::Base`` class.
+ The ``!`` suffix indicates that we want to search for references to the class itself, rather than instances of the class.
+- ``Method[get]`` selects calls to the ``get`` method of the ``Sinatra::Base`` class.
+- ``Argument[block]`` selects the block argument to the ``get`` method call.
+- ``Parameter[0]`` selects the first parameter of the block argument (the parameter named ``x``).
+- Finally, the kind ``remote`` indicates that this is considered a source of remote flow.
Example: Using types to add MySQL injection sinks
-------------------------------------------------
@@ -110,7 +111,7 @@ In this example, we'll show how to add the following SQL injection sink:
client.query(q) # <-- add 'q' as a SQL injection sink
end
-We can recognize this using the following extension:
+We need to add a tuple to the ``sinkModel(type, path, kind)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -121,16 +122,16 @@ We can recognize this using the following extension:
data:
- ["Mysql2::Client", "Method[query].Argument[0]", "sql-injection"]
-- The first column, **"Mysql2::Client"**, begins the search at any instance of the **Mysql2::Client** class.
-- **Method[query]** selects any call to the **query** method on that instance.
-- **Argument[0]** selects the first argument to the method call.
-- **sql-injection** indicates that this is considered a sink for the SQL injection query.
+- The first column, ``"Mysql2::Client"``, begins the search at any instance of the ``Mysql2::Client`` class.
+- ``Method[query]`` selects any call to the ``query`` method on that instance.
+- ``Argument[0]`` selects the first argument to the method call.
+- ``sql-injection`` indicates that this is considered a sink for the SQL injection query.
Continued example: Using type models
------------------------------------
Consider this variation on the previous example, the mysql2 EventMachine API is used.
-The client is obtained via a call to **Mysql2::EM::Client.new**.
+The client is obtained via a call to ``Mysql2::EM::Client.new``.
.. code-block:: ruby
@@ -139,10 +140,10 @@ The client is obtained via a call to **Mysql2::EM::Client.new**.
client.query(q)
end
-So far we have only one model for **Mysql2::Client**, but in the real world we
-may have many models for the various methods available. Because **Mysql2::EM::Client** is a subclass of **Mysql2::Client**, it inherits all of the same methods.
-Instead of updating all our models to include both classes, we can add a type
-model to indicate that **Mysql2::EM::Client** is a subclass of **Mysql2::Client**:
+So far we have only one model for ``Mysql2::Client``, but in the real world we
+may have many models for the various methods available. Because ``Mysql2::EM::Client`` is a subclass of ``Mysql2::Client``, it inherits all of the same methods.
+Instead of updating all our models to include both classes, we can add a tuple to the ``typeModel(type, subtype, ext)`` extensible predicate to indicate that
+``Mysql2::EM::Client`` is a subclass of ``Mysql2::Client``:
.. code-block:: yaml
@@ -162,7 +163,7 @@ In this example, we'll show how to add flow through calls to 'URI.decode_uri_com
y = URI.decode_uri_component(x); # add taint flow from 'x' to 'y'
-We can model this using the following data extension:
+We need to add a tuple to the ``summaryModel(type, path, input, output, kind)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -179,28 +180,26 @@ We can model this using the following data extension:
"taint",
]
-
-- Since we're adding flow through a method call, we add a tuple to the **summaryModel** extensible predicate.
-- The first column, **"URI!"**, begins the search for relevant calls at references to the **URI** class.
-- The **!** suffix indicates that we are looking for the class itself, rather than instances of the class.
-- The second column, **Method[decode_uri_component]**, is a path leading to the method calls we wish to model.
- In this case, we select references to the **decode_uri_component** method from the **URI** class.
-- The third column, **Argument[0]**, indicates the input of the flow. In this case, the first argument to the method call.
-- The fourth column, **ReturnValue**, indicates the output of the flow. In this case, the return value of the method call.
-- The last column, **taint**, indicates the kind of flow to add. The value **taint** means the output is not necessarily equal
+- The first column, ``"URI!"``, begins the search for relevant calls at references to the ``URI`` class.
+ The ``!`` suffix indicates that we are looking for the class itself, rather than instances of the class.
+- The second column, ``Method[decode_uri_component]``, is a path leading to the method calls we wish to model.
+ In this case, we select references to the ``decode_uri_component`` method from the ``URI`` class.
+- The third column, ``Argument[0]``, indicates the input of the flow. In this case, the first argument to the method call.
+- The fourth column, ``ReturnValue``, indicates the output of the flow. In this case, the return value of the method call.
+- The last column, ``taint``, indicates the kind of flow to add. The value ``taint`` means the output is not necessarily equal
to the input, but was derived from the input in a taint-preserving way.
Example: Adding flow through 'File#each'
----------------------------------------
-In this example, we'll show how to add flow through calls to **File#each** from the standard library, which iterates over the lines of a file:
+In this example, we'll show how to add flow through calls to ``File#each`` from the standard library, which iterates over the lines of a file:
.. code-block:: ruby
f = File.new("example.txt")
f.each { |line| ... } # add taint flow from `f` to `line`
-We can model this using the following data extension:
+We need to add a tuple to the ``summaryModel(type, path, input, output, kind)`` extensible predicate by updating a data extension file.
.. code-block:: yaml
@@ -217,18 +216,73 @@ We can model this using the following data extension:
"taint",
]
-
-- Since we're adding flow through a method call, we add a tuple to the **summaryModel** extensible predicate.
-- The first column, **"File"**, begins the search for relevant calls at places where the **File** class is used.
-- The second column, **Method[each]**, selects references to the **each** method on the **File** class.
-- The third column specifies the input of the flow. **Argument[self]** selects the **self** argument of **each**, which is the **File** instance being iterated over.
+- The first column, ``"File"``, begins the search for relevant calls at places where the ``File`` class is used.
+- The second column, ``Method[each]``, selects references to the ``each`` method on the ``File`` class.
+- The third column specifies the input of the flow. ``Argument[self]`` selects the ``self`` argument of ``each``, which is the ``File`` instance being iterated over.
- The fourth column specifies the output of the flow:
- - **Argument[block]** selects the block argument of **each** (the block which is executed for each line in the file).
- - **Parameter[0]** selects the first parameter of the block (the parameter named **line**).
+ - ``Argument[block]`` selects the block argument of ``each`` (the block which is executed for each line in the file).
+ - ``Parameter[0]`` selects the first parameter of the block (the parameter named ``line``).
-- The last column, **taint**, indicates the kind of flow to add.
+- The last column, ``taint``, indicates the kind of flow to add.
+
+Example: Taint barrier using the 'escape' method
+------------------------------------------------
+
+In this example, we'll show how to add the return value of ``Mysql2::Client#escape`` as a barrier for SQL injection.
+
+.. code-block:: ruby
+
+ client = Mysql2::Client.new
+ escaped = client.escape(input) # The return value of this method is safe for SQL injection.
+ client.query("SELECT * FROM users WHERE name = '#{escaped}'")
+
+We need to add a tuple to the ``barrierModel(type, path, kind)`` extensible predicate by updating a data extension file.
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/ruby-all
+ extensible: barrierModel
+ data:
+ - ["Mysql2::Client!", "Method[escape].ReturnValue", "sql-injection"]
+
+- The first column, ``"Mysql2::Client!"``, begins the search for relevant calls at references to the ``Mysql2::Client`` class.
+ The ``!`` suffix indicates that we want to search for references to the class itself, rather than instances of the class.
+- The second column, ``"Method[escape].ReturnValue"``, selects the return value of the ``escape`` method.
+- The third column, ``"sql-injection"``, is the kind of the barrier.
+
+Example: Add a barrier guard
+----------------------------
+
+This example shows how to model a barrier guard that stops the flow of taint when a conditional check is performed on data.
+Consider a validation method ``Validator.is_safe`` which returns ``true`` when the data is considered safe.
+
+.. code-block:: ruby
+
+ if Validator.is_safe(user_input)
+ # The check guards the use, so the input is safe.
+ client.query("SELECT * FROM users WHERE name = '#{user_input}'")
+ end
+
+We need to add a tuple to the ``barrierGuardModel(type, path, acceptingValue, kind)`` extensible predicate by updating a data extension file.
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/ruby-all
+ extensible: barrierGuardModel
+ data:
+ - ["Validator!", "Method[is_safe].Argument[0]", "true", "sql-injection"]
+
+- The first column, ``"Validator!"``, begins the search at references to the ``Validator`` class.
+ The ``!`` suffix indicates that we want to search for references to the class itself, rather than instances of the class.
+- The second column, ``"Method[is_safe].Argument[0]"``, selects the first argument of the ``is_safe`` method. This is the value being validated.
+- The third column, ``"true"``, is the accepting value of the barrier guard. This is the value that the conditional check must return for the barrier to apply.
+- The fourth column, ``"sql-injection"``, is the kind of the barrier guard.
Reference material
------------------
@@ -243,9 +297,9 @@ sourceModel(type, path, kind)
Adds a new taint source. Most taint-tracking queries will use the new source.
-- **type**: Name of a type from which to evaluate **path**.
-- **path**: Access path leading to the source.
-- **kind**: Kind of source to add. Currently only **remote** is used.
+- ``type``: Name of a type from which to evaluate ``path``.
+- ``path``: Access path leading to the source.
+- ``kind``: Kind of source to add. Currently only ``remote`` is used.
Example:
@@ -263,9 +317,9 @@ sinkModel(type, path, kind)
Adds a new taint sink. Sinks are query-specific and will typically affect one or two queries.
-- **type**: Name of a type from which to evaluate **path**.
-- **path**: Access path leading to the sink.
-- **kind**: Kind of sink to add. See the section on sink kinds for a list of supported kinds.
+- ``type``: Name of a type from which to evaluate ``path``.
+- ``path``: Access path leading to the sink.
+- ``kind``: Kind of sink to add. See the section on sink kinds for a list of supported kinds.
Example:
@@ -283,11 +337,11 @@ summaryModel(type, path, input, output, kind)
Adds flow through a method call.
-- **type**: Name of a type from which to evaluate **path**.
-- **path**: Access path leading to a method call.
-- **input**: Path relative to the method call that leads to input of the flow.
-- **output**: Path relative to the method call leading to the output of the flow.
-- **kind**: Kind of summary to add. Can be **taint** for taint-propagating flow, or **value** for value-preserving flow.
+- ``type``: Name of a type from which to evaluate ``path``.
+- ``path``: Access path leading to a method call.
+- ``input``: Path relative to the method call that leads to input of the flow.
+- ``output``: Path relative to the method call leading to the output of the flow.
+- ``kind``: Kind of summary to add. Can be ``taint`` for taint-propagating flow, or ``value`` for value-preserving flow.
Example:
@@ -311,9 +365,9 @@ typeModel(type1, type2, path)
Adds a new definition of a type.
-- **type1**: Name of the type to define.
-- **type2**: Name of the type from which to evaluate **path**.
-- **path**: Access path leading from **type2** to **type1**.
+- ``type1``: Name of the type to define.
+- ``type2``: Name of the type from which to evaluate ``path``.
+- ``path``: Access path leading from ``type2`` to ``type1``.
Example:
@@ -335,44 +389,44 @@ Types
A type is a string that identifies a set of values.
In each of the extensible predicates mentioned in previous section, the first column is always the name of a type.
-A type can be defined by adding **typeModel** tuples for that type.
+A type can be defined by adding ``typeModel`` tuples for that type.
Access paths
------------
-The **path**, **input**, and **output** columns consist of a **.**-separated list of components, which is evaluated from left to right,
+The ``path``, ``input``, and ``output`` columns consist of a ``.``-separated list of components, which is evaluated from left to right,
with each step selecting a new set of values derived from the previous set of values.
The following components are supported:
-- **Argument[**\ `number`\ **]** selects the argument at the given index.
-- **Argument[**\ `string`:\ **]** selects the keyword argument with the given name.
-- **Argument[self]** selects the receiver of a method call.
-- **Argument[block]** selects the block argument.
-- **Argument[any]** selects any argument, except self or block arguments.
-- **Argument[any-named]** selects any keyword argument.
-- **Argument[hash-splat]** selects a special argument representing all keyword arguments passed in the method call.
-- **Parameter[**\ `number`\ **]** selects the argument at the given index.
-- **Parameter[**\ `string`:\ **]** selects the keyword argument with the given name.
-- **Parameter[self]** selects the **self** parameter of a method.
-- **Parameter[block]** selects the block parameter.
-- **Parameter[any]** selects any parameter, except self or block parameters.
-- **Parameter[any-named]** selects any keyword parameter.
-- **Parameter[hash-splat]** selects the hash splat parameter, often written as **\*\*kwargs**.
-- **ReturnValue** selects the return value of a call.
-- **Method[**\ `name`\ **]** selects a call to the method with the given name.
-- **Element[any]** selects any element of an array or hash.
-- **Element[**\ `number`\ **]** selects an array element at the given index.
-- **Element[**\ `string`\ **]** selects a hash element at the given key.
-- **Field[@**\ `string`\ **]** selects an instance variable with the given name.
-- **Fuzzy** selects all values that are derived from the current value through a combination of the other operations described in this list.
+- ``Argument[``\ `number`\ ``]`` selects the argument at the given index.
+- ``Argument[``\ `string`:\ ``]`` selects the keyword argument with the given name.
+- ``Argument[self]`` selects the receiver of a method call.
+- ``Argument[block]`` selects the block argument.
+- ``Argument[any]`` selects any argument, except self or block arguments.
+- ``Argument[any-named]`` selects any keyword argument.
+- ``Argument[hash-splat]`` selects a special argument representing all keyword arguments passed in the method call.
+- ``Parameter[``\ `number`\ ``]`` selects the argument at the given index.
+- ``Parameter[``\ `string`:\ ``]`` selects the keyword argument with the given name.
+- ``Parameter[self]`` selects the ``self`` parameter of a method.
+- ``Parameter[block]`` selects the block parameter.
+- ``Parameter[any]`` selects any parameter, except self or block parameters.
+- ``Parameter[any-named]`` selects any keyword parameter.
+- ``Parameter[hash-splat]`` selects the hash splat parameter, often written as **\*\*kwargs**.
+- ``ReturnValue`` selects the return value of a call.
+- ``Method[``\ `name`\ ``]`` selects a call to the method with the given name.
+- ``Element[any]`` selects any element of an array or hash.
+- ``Element[``\ `number`\ ``]`` selects an array element at the given index.
+- ``Element[``\ `string`\ ``]`` selects a hash element at the given key.
+- ``Field[@``\ `string`\ ``]`` selects an instance variable with the given name.
+- ``Fuzzy`` selects all values that are derived from the current value through a combination of the other operations described in this list.
For example, this can be used to find all values that appear to originate from a particular class. This can be useful for finding method calls
from a known class, but where the receiver type is not known or is difficult to model.
Additional notes about the syntax of operands:
-- Multiple operands may be given to a single component, as a shorthand for the union of the operands. For example, **Method[foo,bar]** matches the union of **Method[foo]** and **Method[bar]**.
-- Numeric operands to **Argument**, **Parameter**, and **Element** may be given as a lower bound. For example, **Argument[1..]** matches all arguments except 0.
+- Multiple operands may be given to a single component, as a shorthand for the union of the operands. For example, ``Method[foo,bar]`` matches the union of ``Method[foo]`` and ``Method[bar]``.
+- Numeric operands to ``Argument``, ``Parameter``, and ``Element`` may be given as a lower bound. For example, ``Argument[1..]`` matches all arguments except 0.
Kinds
-----
@@ -380,7 +434,7 @@ Kinds
Source kinds
~~~~~~~~~~~~
-- **remote**: A generic source of remote flow. Most taint-tracking queries will use such a source. Currently this is the only supported source kind.
+- ``remote``: A generic source of remote flow. Most taint-tracking queries will use such a source. Currently this is the only supported source kind.
Sink kinds
~~~~~~~~~~
@@ -388,15 +442,15 @@ Sink kinds
Unlike sources, sinks tend to be highly query-specific, rarely affecting more than one or two queries.
Not every query supports customizable sinks. If the following sinks are not suitable for your use case, you should add a new query.
-- **code-injection**: A sink that can be used to inject code, such as in calls to **eval**.
-- **command-injection**: A sink that can be used to inject shell commands, such as in calls to **Process.spawn**.
-- **path-injection**: A sink that can be used for path injection in a file system access, such as in calls to **File.open**.
-- **sql-injection**: A sink that can be used for SQL injection, such as in an ActiveRecord **where** call.
-- **url-redirection**: A sink that can be used to redirect the user to a malicious URL.
-- **log-injection**: A sink that can be used for log injection, such as in a **Rails.logger** call.
+- ``code-injection``: A sink that can be used to inject code, such as in calls to ``eval``.
+- ``command-injection``: A sink that can be used to inject shell commands, such as in calls to ``Process.spawn``.
+- ``path-injection``: A sink that can be used for path injection in a file system access, such as in calls to ``File.open``.
+- ``sql-injection``: A sink that can be used for SQL injection, such as in an ActiveRecord ``where`` call.
+- ``url-redirection``: A sink that can be used to redirect the user to a malicious URL.
+- ``log-injection``: A sink that can be used for log injection, such as in a ``Rails.logger`` call.
Summary kinds
~~~~~~~~~~~~~
-- **taint**: A summary that propagates taint. This means the output is not necessarily equal to the input, but it was derived from the input in an unrestrictive way. An attacker who controls the input will have significant control over the output as well.
-- **value**: A summary that preserves the value of the input or creates a copy of the input such that all of its object properties are preserved.
+- ``taint``: A summary that propagates taint. This means the output is not necessarily equal to the input, but it was derived from the input in an unrestrictive way. An attacker who controls the input will have significant control over the output as well.
+- ``value``: A summary that preserves the value of the input or creates a copy of the input such that all of its object properties are preserved.
diff --git a/docs/codeql/codeql-overview/codeql-changelog/codeql-cli-2.25.2.rst b/docs/codeql/codeql-overview/codeql-changelog/codeql-cli-2.25.2.rst
new file mode 100644
index 00000000000..58c6a7245d2
--- /dev/null
+++ b/docs/codeql/codeql-overview/codeql-changelog/codeql-cli-2.25.2.rst
@@ -0,0 +1,157 @@
+.. _codeql-cli-2.25.2:
+
+==========================
+CodeQL 2.25.2 (2026-04-15)
+==========================
+
+.. contents:: Contents
+ :depth: 2
+ :local:
+ :backlinks: none
+
+This is an overview of changes in the CodeQL CLI and relevant CodeQL query and library packs. For additional updates on changes to the CodeQL code scanning experience, check out the `code scanning section on the GitHub blog `__, `relevant GitHub Changelog updates `__, `changes in the CodeQL extension for Visual Studio Code `__, and the `CodeQL Action changelog `__.
+
+Security Coverage
+-----------------
+
+CodeQL 2.25.2 runs a total of 492 security queries when configured with the Default suite (covering 166 CWE). The Extended suite enables an additional 135 queries (covering 35 more CWE). 1 security query has been added with this release.
+
+CodeQL CLI
+----------
+
+Miscellaneous
+~~~~~~~~~~~~~
+
+* The build of Eclipse Temurin OpenJDK that is used to run the CodeQL CLI has been updated to version 21.0.10.
+
+Query Packs
+-----------
+
+Major Analysis Improvements
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+C#
+""
+
+* The :code:`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 :code:`cs/constant-comparison` used to report, and as consequence, that query has been deleted.
+
+Python
+""""""
+
+* Several quality queries have been ported away from using the legacy points-to library. This may lead to changes in alerts.
+
+Minor Analysis Improvements
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+C/C++
+"""""
+
+* The "Extraction warnings" (:code:`cpp/diagnostics/extraction-warnings`) diagnostics query no longer yields :code:`ExtractionRecoverableWarning`\ s for :code:`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" (:code:`cpp/suspicious-add-sizeof`) query causing false positive results in :code:`build-mode: none` databases.
+* Fixed an issue with the "Uncontrolled format string" (:code:`cpp/tainted-format-string`) query involving certain kinds of formatting function implementations.
+* Fixed an issue with the "Wrong type of arguments to formatting function" (:code:`cpp/wrong-type-format-argument`) query causing false positive results in :code:`build-mode: none` databases.
+* Fixed an issue with the "Multiplication result converted to larger type" (:code:`cpp/integer-multiplication-cast-to-long`) query causing false positive results in :code:`build-mode: none` databases.
+
+Query Metadata Changes
+~~~~~~~~~~~~~~~~~~~~~~
+
+C/C++
+"""""
+
+* The :code:`@security-severity` metadata of :code:`cpp/cgi-xss` has been increased from 6.1 (medium) to 7.8 (high).
+
+C#
+""
+
+* The :code:`@security-severity` metadata of :code:`cs/log-forging` has been reduced from 7.8 (high) to 6.1 (medium).
+* The :code:`@security-severity` metadata of :code:`cs/web/xss` has been increased from 6.1 (medium) to 7.8 (high).
+
+Golang
+""""""
+
+* The :code:`@security-severity` metadata of :code:`go/log-injection` has been reduced from 7.8 (high) to 6.1 (medium).
+* The :code:`@security-severity` metadata of :code:`go/html-template-escaping-bypass-xss`, :code:`go/reflected-xss` and :code:`go/stored-xss` has been increased from 6.1 (medium) to 7.8 (high).
+
+Java/Kotlin
+"""""""""""
+
+* The :code:`@security-severity` metadata of :code:`java/log-injection` has been reduced from 7.8 (high) to 6.1 (medium).
+* The :code:`@security-severity` metadata of :code:`java/android/webview-addjavascriptinterface`, :code:`java/android/websettings-javascript-enabled` and :code:`java/xss` has been increased from 6.1 (medium) to 7.8 (high).
+
+Python
+""""""
+
+* The :code:`@security-severity` metadata of :code:`py/log-injection` has been reduced from 7.8 (high) to 6.1 (medium).
+* The :code:`@security-severity` metadata of :code:`py/jinja2/autoescape-false` and :code:`py/reflective-xss` has been increased from 6.1 (medium) to 7.8 (high).
+
+Ruby
+""""
+
+* The :code:`@security-severity` metadata of :code:`rb/log-injection` has been reduced from 7.8 (high) to 6.1 (medium).
+* The :code:`@security-severity` metadata of :code:`rb/reflected-xss`, :code:`rb/stored-xss` and :code:`rb/html-constructed-from-input` has been increased from 6.1 (medium) to 7.8 (high).
+
+Swift
+"""""
+
+* The :code:`@security-severity` metadata of :code:`swift/unsafe-webview-fetch` has been increased from 6.1 (medium) to 7.8 (high).
+
+Rust
+""""
+
+* The :code:`@security-severity` metadata of :code:`rust/log-injection` has been increased from 2.6 (low) to 6.1 (medium).
+* The :code:`@security-severity` metadata of :code:`rust/xss` has been increased from 6.1 (medium) to 7.8 (high).
+
+Language Libraries
+------------------
+
+Bug Fixes
+~~~~~~~~~
+
+Python
+""""""
+
+* Fixed the resolution of relative imports such as :code:`from . import helper` inside namespace packages (directories without an :code:`__init__.py` file), which previously did not work correctly, leading to missing flow.
+
+Breaking Changes
+~~~~~~~~~~~~~~~~
+
+C/C++
+"""""
+
+* The :code:`SourceModelCsv`, :code:`SinkModelCsv`, and :code:`SummaryModelCsv` classes and the associated CSV parsing infrastructure have been removed from :code:`ExternalFlow.qll`. New models should be added as :code:`.model.yml` files in the :code:`ext/` directory.
+
+Minor Analysis Improvements
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+C/C++
+"""""
+
+* Added :code:`HttpReceiveHttpRequest`, :code:`HttpReceiveRequestEntityBody`, and :code:`HttpReceiveClientCertificate` from Win32's :code:`http.h` as remote flow sources.
+* Added dataflow through members initialized via non-static data member initialization (NSDMI).
+
+C#
+""
+
+* 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 :code:`cs/log-forging` query no longer treats arguments to extension methods with source code on :code:`ILogger` types as sinks. Instead, taint is tracked interprocedurally through extension method bodies, reducing false positives when extension methods sanitize input internally.
+
+Java/Kotlin
+"""""""""""
+
+* The :code:`java/tainted-arithmetic` query no longer flags arithmetic expressions that are used directly as an operand of a comparison in :code:`if`\ -condition bounds-checking patterns. For example, :code:`if (off + len > array.length)` is now recognized as a bounds check rather than a potentially vulnerable computation, reducing false positives.
+* The :code:`java/potentially-weak-cryptographic-algorithm` query no longer flags Elliptic Curve algorithms (:code:`EC`, :code:`ECDSA`, :code:`ECDH`, :code:`EdDSA`, :code:`Ed25519`, :code:`Ed448`, :code:`XDH`, :code:`X25519`, :code:`X448`), HMAC-based algorithms (:code:`HMACSHA1`, :code:`HMACSHA256`, :code:`HMACSHA384`, :code:`HMACSHA512`), or PBKDF2 key derivation as potentially insecure. These are modern, secure algorithms recommended by NIST and other standards bodies. This will reduce the number of false positives for this query.
+* The first argument of the method :code:`getInstance` of :code:`java.security.Signature` is now modeled as a sink for :code:`java/potentially-weak-cryptographic-algorithm`, :code:`java/weak-cryptographic-algorithm` and :code:`java/rsa-without-oaep`. This will increase the number of alerts for these queries.
+* Kotlin versions up to 2.3.20 are now supported.
+
+New Features
+~~~~~~~~~~~~
+
+C/C++
+"""""
+
+* Added a subclass :code:`MesonPrivateTestFile` of :code:`ConfigurationTestFile` that represents files created by Meson to test the build configuration.
+* Added a class :code:`ConstructorDirectFieldInit` to represent field initializations that occur in member initializer lists.
+* Added a class :code:`ConstructorDefaultFieldInit` to represent default field initializations.
+* Added a class :code:`DataFlow::IndirectParameterNode` to represent the indirection of a parameter as a dataflow node.
+* Added a predicate :code:`Node::asIndirectInstruction` which returns the :code:`Instruction` that defines the indirect dataflow node, if any.
+* Added a class :code:`IndirectUninitializedNode` to represent the indirection of an uninitialized local variable as a dataflow node.
diff --git a/docs/codeql/codeql-overview/codeql-changelog/index.rst b/docs/codeql/codeql-overview/codeql-changelog/index.rst
index fc3c5247328..32a8b4574bb 100644
--- a/docs/codeql/codeql-overview/codeql-changelog/index.rst
+++ b/docs/codeql/codeql-overview/codeql-changelog/index.rst
@@ -11,6 +11,7 @@ A list of queries for each suite and language `is available here {
predicate uniqueNodeLocationExclude(DataFlow::Node n) { missingLocationExclude(n) }
- predicate localFlowIsLocalExclude(DataFlow::Node n1, DataFlow::Node n2) {
- n1 instanceof DataFlow::FunctionNode and simpleLocalFlowStep(n1, n2, _)
- }
-
predicate argHasPostUpdateExclude(DataFlow::ArgumentNode n) {
not DataFlow::insnHasPostUpdateNode(n.asInstruction())
}
diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll
index 33149bf0057..2c5c64ace7d 100644
--- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll
+++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll
@@ -95,10 +95,6 @@ predicate basicLocalFlowStep(Node nodeFrom, Node nodeTo) {
nodeTo = instructionNode(succ) and
nodeTo != nodeFrom
)
- or
- // GlobalFunctionNode -> use
- nodeFrom =
- any(GlobalFunctionNode fn | fn.getFunction() = nodeTo.asExpr().(FunctionName).getTarget())
}
pragma[noinline]
diff --git a/go/ql/lib/semmle/go/dataflow/internal/ExternalFlowExtensions.qll b/go/ql/lib/semmle/go/dataflow/internal/ExternalFlowExtensions.qll
index 5d43cf674c1..ab2a241e14a 100644
--- a/go/ql/lib/semmle/go/dataflow/internal/ExternalFlowExtensions.qll
+++ b/go/ql/lib/semmle/go/dataflow/internal/ExternalFlowExtensions.qll
@@ -35,7 +35,7 @@ extensible predicate barrierModel(
*/
extensible predicate barrierGuardModel(
string package, string type, boolean subtypes, string name, string signature, string ext,
- string input, string acceptingvalue, string kind, string provenance, QlBuiltins::ExtensionId madId
+ string input, string acceptingValue, string kind, string provenance, QlBuiltins::ExtensionId madId
);
/**
diff --git a/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll b/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll
index 240665bd492..ff727286c3b 100644
--- a/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll
+++ b/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll
@@ -174,13 +174,13 @@ module SourceSinkInterpretationInput implements
}
predicate barrierGuardElement(
- Element e, string input, Public::AcceptingValue acceptingvalue, string kind,
+ Element e, string input, Public::AcceptingValue acceptingValue, string kind,
Public::Provenance provenance, string model
) {
exists(
string package, string type, boolean subtypes, string name, string signature, string ext
|
- barrierGuardModel(package, type, subtypes, name, signature, ext, input, acceptingvalue, kind,
+ barrierGuardModel(package, type, subtypes, name, signature, ext, input, acceptingValue, kind,
provenance, model) and
e = interpretElement(package, type, subtypes, name, signature, ext)
)
diff --git a/go/ql/lib/semmle/go/frameworks/stdlib/IoFs.qll b/go/ql/lib/semmle/go/frameworks/stdlib/IoFs.qll
index b071e56cbb5..cfd8a8aee23 100644
--- a/go/ql/lib/semmle/go/frameworks/stdlib/IoFs.qll
+++ b/go/ql/lib/semmle/go/frameworks/stdlib/IoFs.qll
@@ -20,10 +20,13 @@ module IoFs {
private class WalkDirStep extends TaintTracking::AdditionalTaintStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
//signature: func WalkDir(fsys FS, root string, fn WalkDirFunc) error
- exists(DataFlow::CallNode call, DataFlow::FunctionNode f |
- call.getTarget().hasQualifiedName(packagePath(), "WalkDir") and
- f.getASuccessor*() = call.getArgument(2)
+ exists(DataFlow::CallNode call, DataFlow::FunctionNode f, DataFlow::Node n |
+ n = f.(DataFlow::FuncLitNode)
+ or
+ n.asExpr().(FunctionName).getTarget() = f.(DataFlow::GlobalFunctionNode).getFunction()
|
+ call.getTarget().hasQualifiedName(packagePath(), "WalkDir") and
+ n.getASuccessor*() = call.getArgument(2) and
pred = call.getArgument(0) and
succ = f.getParameter([0, 1])
)
diff --git a/go/ql/src/CHANGELOG.md b/go/ql/src/CHANGELOG.md
index f6fcaa51ab3..971d478d56e 100644
--- a/go/ql/src/CHANGELOG.md
+++ b/go/ql/src/CHANGELOG.md
@@ -1,3 +1,14 @@
+## 1.6.1
+
+No user-facing changes.
+
+## 1.6.0
+
+### Query Metadata Changes
+
+* The `@security-severity` metadata of `go/log-injection` has been reduced from 7.8 (high) to 6.1 (medium).
+* The `@security-severity` metadata of `go/html-template-escaping-bypass-xss`, `go/reflected-xss` and `go/stored-xss` has been increased from 6.1 (medium) to 7.8 (high).
+
## 1.5.10
No user-facing changes.
diff --git a/go/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md b/go/ql/src/change-notes/released/1.6.0.md
similarity index 87%
rename from go/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md
rename to go/ql/src/change-notes/released/1.6.0.md
index 45320bcd719..1e508254885 100644
--- a/go/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md
+++ b/go/ql/src/change-notes/released/1.6.0.md
@@ -1,5 +1,6 @@
----
-category: queryMetadata
----
+## 1.6.0
+
+### Query Metadata Changes
+
* The `@security-severity` metadata of `go/log-injection` has been reduced from 7.8 (high) to 6.1 (medium).
* The `@security-severity` metadata of `go/html-template-escaping-bypass-xss`, `go/reflected-xss` and `go/stored-xss` has been increased from 6.1 (medium) to 7.8 (high).
diff --git a/go/ql/src/change-notes/released/1.6.1.md b/go/ql/src/change-notes/released/1.6.1.md
new file mode 100644
index 00000000000..898f6201ed7
--- /dev/null
+++ b/go/ql/src/change-notes/released/1.6.1.md
@@ -0,0 +1,3 @@
+## 1.6.1
+
+No user-facing changes.
diff --git a/go/ql/src/codeql-pack.release.yml b/go/ql/src/codeql-pack.release.yml
index fda54b31bff..ef7a789e0cf 100644
--- a/go/ql/src/codeql-pack.release.yml
+++ b/go/ql/src/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 1.5.10
+lastReleaseVersion: 1.6.1
diff --git a/go/ql/src/qlpack.yml b/go/ql/src/qlpack.yml
index fd374637a9b..fa7e934382a 100644
--- a/go/ql/src/qlpack.yml
+++ b/go/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/go-queries
-version: 1.5.11-dev
+version: 1.6.2-dev
groups:
- go
- queries
diff --git a/go/ql/test/library-tests/semmle/go/dataflow/FlowSteps/LocalFlowStep.expected b/go/ql/test/library-tests/semmle/go/dataflow/FlowSteps/LocalFlowStep.expected
index fcbb78716a4..7fa8b681d7f 100644
--- a/go/ql/test/library-tests/semmle/go/dataflow/FlowSteps/LocalFlowStep.expected
+++ b/go/ql/test/library-tests/semmle/go/dataflow/FlowSteps/LocalFlowStep.expected
@@ -1,52 +1,3 @@
-| file://:0:0:0:0 | function Encode | url.go:51:14:51:21 | selection of Encode |
-| file://:0:0:0:0 | function EscapedPath | url.go:28:14:28:26 | selection of EscapedPath |
-| file://:0:0:0:0 | function Get | url.go:52:14:52:18 | selection of Get |
-| file://:0:0:0:0 | function Hostname | url.go:29:14:29:23 | selection of Hostname |
-| file://:0:0:0:0 | function JoinPath | url.go:57:16:57:27 | selection of JoinPath |
-| file://:0:0:0:0 | function JoinPath | url.go:58:16:58:27 | selection of JoinPath |
-| file://:0:0:0:0 | function JoinPath | url.go:60:15:60:28 | selection of JoinPath |
-| file://:0:0:0:0 | function JoinPath | url.go:66:9:66:25 | selection of JoinPath |
-| file://:0:0:0:0 | function MarshalBinary | url.go:30:11:30:25 | selection of MarshalBinary |
-| file://:0:0:0:0 | function Parse | url.go:23:10:23:18 | selection of Parse |
-| file://:0:0:0:0 | function Parse | url.go:32:9:32:15 | selection of Parse |
-| file://:0:0:0:0 | function Parse | url.go:59:14:59:22 | selection of Parse |
-| file://:0:0:0:0 | function Parse | url.go:65:17:65:25 | selection of Parse |
-| file://:0:0:0:0 | function ParseQuery | url.go:50:10:50:23 | selection of ParseQuery |
-| file://:0:0:0:0 | function ParseRequestURI | url.go:27:9:27:27 | selection of ParseRequestURI |
-| file://:0:0:0:0 | function Password | url.go:43:11:43:21 | selection of Password |
-| file://:0:0:0:0 | function PathEscape | url.go:12:31:12:44 | selection of PathEscape |
-| file://:0:0:0:0 | function PathUnescape | url.go:12:14:12:29 | selection of PathUnescape |
-| file://:0:0:0:0 | function Port | url.go:33:14:33:19 | selection of Port |
-| file://:0:0:0:0 | function Println | url.go:28:2:28:12 | selection of Println |
-| file://:0:0:0:0 | function Println | url.go:29:2:29:12 | selection of Println |
-| file://:0:0:0:0 | function Println | url.go:31:2:31:12 | selection of Println |
-| file://:0:0:0:0 | function Println | url.go:33:2:33:12 | selection of Println |
-| file://:0:0:0:0 | function Println | url.go:34:2:34:12 | selection of Println |
-| file://:0:0:0:0 | function Println | url.go:35:2:35:12 | selection of Println |
-| file://:0:0:0:0 | function Println | url.go:44:2:44:12 | selection of Println |
-| file://:0:0:0:0 | function Println | url.go:45:2:45:12 | selection of Println |
-| file://:0:0:0:0 | function Println | url.go:51:2:51:12 | selection of Println |
-| file://:0:0:0:0 | function Println | url.go:52:2:52:12 | selection of Println |
-| file://:0:0:0:0 | function Query | url.go:34:14:34:20 | selection of Query |
-| file://:0:0:0:0 | function QueryEscape | url.go:14:32:14:46 | selection of QueryEscape |
-| file://:0:0:0:0 | function QueryUnescape | url.go:14:14:14:30 | selection of QueryUnescape |
-| file://:0:0:0:0 | function Replace | strings.go:9:8:9:22 | selection of Replace |
-| file://:0:0:0:0 | function ReplaceAll | strings.go:10:8:10:25 | selection of ReplaceAll |
-| file://:0:0:0:0 | function RequestURI | url.go:35:14:35:25 | selection of RequestURI |
-| file://:0:0:0:0 | function ResolveReference | url.go:36:6:36:23 | selection of ResolveReference |
-| file://:0:0:0:0 | function Sprint | strings.go:11:9:11:18 | selection of Sprint |
-| file://:0:0:0:0 | function Sprintf | strings.go:11:30:11:40 | selection of Sprintf |
-| file://:0:0:0:0 | function Sprintln | strings.go:11:54:11:65 | selection of Sprintln |
-| file://:0:0:0:0 | function User | url.go:41:8:41:15 | selection of User |
-| file://:0:0:0:0 | function UserPassword | url.go:42:7:42:22 | selection of UserPassword |
-| file://:0:0:0:0 | function Username | url.go:45:14:45:24 | selection of Username |
-| file://:0:0:0:0 | function append | main.go:39:8:39:13 | append |
-| file://:0:0:0:0 | function append | main.go:40:8:40:13 | append |
-| file://:0:0:0:0 | function copy | main.go:42:2:42:5 | copy |
-| file://:0:0:0:0 | function make | main.go:41:8:41:11 | make |
-| file://:0:0:0:0 | function max | main.go:65:7:65:9 | max |
-| file://:0:0:0:0 | function min | main.go:64:7:64:9 | min |
-| main.go:3:6:3:10 | function test1 | main.go:34:2:34:6 | test1 |
| main.go:3:12:3:12 | argument corresponding to x | main.go:3:12:3:12 | definition of x |
| main.go:3:12:3:12 | definition of x | main.go:5:5:5:5 | x |
| main.go:3:19:3:20 | argument corresponding to fn | main.go:3:19:3:20 | definition of fn |
@@ -66,8 +17,6 @@
| main.go:10:12:10:12 | y | main.go:10:17:10:17 | y |
| main.go:10:17:10:27 | ...>=... | main.go:10:7:10:27 | ...&&... |
| main.go:11:14:11:14 | z | main.go:11:9:11:15 | type conversion |
-| main.go:14:6:14:10 | function test2 | main.go:34:8:34:12 | test2 |
-| main.go:14:6:14:10 | function test2 | main.go:34:19:34:23 | test2 |
| main.go:15:9:15:9 | 0 | main.go:15:2:15:4 | definition of acc |
| main.go:16:9:19:2 | capture variable acc | main.go:17:3:17:5 | acc |
| main.go:17:3:17:7 | definition of acc | main.go:18:10:18:12 | acc |
diff --git a/go/ql/test/library-tests/semmle/go/dataflow/PromotedFields/LocalFlowStep.expected b/go/ql/test/library-tests/semmle/go/dataflow/PromotedFields/LocalFlowStep.expected
index 5908aa8d113..3767cd57b5d 100644
--- a/go/ql/test/library-tests/semmle/go/dataflow/PromotedFields/LocalFlowStep.expected
+++ b/go/ql/test/library-tests/semmle/go/dataflow/PromotedFields/LocalFlowStep.expected
@@ -1,83 +1,3 @@
-| main.go:3:6:3:11 | function source | main.go:23:31:23:36 | source |
-| main.go:3:6:3:11 | function source | main.go:31:31:31:36 | source |
-| main.go:3:6:3:11 | function source | main.go:40:30:40:35 | source |
-| main.go:3:6:3:11 | function source | main.go:46:32:46:37 | source |
-| main.go:3:6:3:11 | function source | main.go:54:17:54:22 | source |
-| main.go:3:6:3:11 | function source | main.go:62:18:62:23 | source |
-| main.go:3:6:3:11 | function source | main.go:72:17:72:22 | source |
-| main.go:3:6:3:11 | function source | main.go:80:18:80:23 | source |
-| main.go:3:6:3:11 | function source | main.go:91:16:91:21 | source |
-| main.go:3:6:3:11 | function source | main.go:98:17:98:22 | source |
-| main.go:3:6:3:11 | function source | main.go:107:22:107:27 | source |
-| main.go:3:6:3:11 | function source | main.go:114:23:114:28 | source |
-| main.go:3:6:3:11 | function source | main.go:123:23:123:28 | source |
-| main.go:3:6:3:11 | function source | main.go:130:24:130:29 | source |
-| main.go:3:6:3:11 | function source | main.go:139:29:139:34 | source |
-| main.go:3:6:3:11 | function source | main.go:146:30:146:35 | source |
-| main.go:7:6:7:9 | function sink | main.go:25:2:25:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:26:2:26:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:27:2:27:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:28:2:28:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:33:2:33:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:34:2:34:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:35:2:35:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:36:2:36:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:41:2:41:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:42:2:42:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:43:2:43:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:44:2:44:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:47:2:47:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:48:2:48:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:49:2:49:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:50:2:50:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:57:2:57:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:58:2:58:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:59:2:59:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:60:2:60:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:65:2:65:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:66:2:66:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:67:2:67:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:68:2:68:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:75:2:75:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:76:2:76:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:77:2:77:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:78:2:78:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:83:2:83:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:84:2:84:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:85:2:85:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:86:2:86:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:92:2:92:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:93:2:93:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:94:2:94:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:95:2:95:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:99:2:99:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:100:2:100:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:101:2:101:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:102:2:102:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:108:2:108:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:109:2:109:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:110:2:110:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:111:2:111:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:115:2:115:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:116:2:116:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:117:2:117:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:118:2:118:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:124:2:124:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:125:2:125:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:126:2:126:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:127:2:127:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:131:2:131:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:132:2:132:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:133:2:133:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:134:2:134:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:140:2:140:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:141:2:141:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:142:2:142:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:143:2:143:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:147:2:147:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:148:2:148:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:149:2:149:5 | sink |
-| main.go:7:6:7:9 | function sink | main.go:150:2:150:5 | sink |
| main.go:22:2:22:6 | definition of outer | main.go:25:7:25:11 | outer |
| main.go:22:11:24:2 | struct literal | main.go:22:2:22:6 | definition of outer |
| main.go:22:11:24:2 | struct literal [postupdate] | main.go:22:2:22:6 | definition of outer |
diff --git a/java/ql/lib/CHANGELOG.md b/java/ql/lib/CHANGELOG.md
index ecbffa5461c..2d34c791c92 100644
--- a/java/ql/lib/CHANGELOG.md
+++ b/java/ql/lib/CHANGELOG.md
@@ -1,3 +1,18 @@
+## 9.0.4
+
+### Minor Analysis Improvements
+
+* The queries "Resolving XML external entity in user-controlled data" (`java/xxe`) and "Resolving XML external entity in user-controlled data from local source" (`java/xxe-local`) now recognize sinks in the Woodstox StAX library when `com.ctc.wstx.stax.WstxInputFactory` or `org.codehaus.stax2.XMLInputFactory2` are used directly.
+
+## 9.0.3
+
+### Minor Analysis Improvements
+
+* The `java/tainted-arithmetic` query no longer flags arithmetic expressions that are used directly as an operand of a comparison in `if`-condition bounds-checking patterns. For example, `if (off + len > array.length)` is now recognized as a bounds check rather than a potentially vulnerable computation, reducing false positives.
+* The `java/potentially-weak-cryptographic-algorithm` query no longer flags Elliptic Curve algorithms (`EC`, `ECDSA`, `ECDH`, `EdDSA`, `Ed25519`, `Ed448`, `XDH`, `X25519`, `X448`), HMAC-based algorithms (`HMACSHA1`, `HMACSHA256`, `HMACSHA384`, `HMACSHA512`), or PBKDF2 key derivation as potentially insecure. These are modern, secure algorithms recommended by NIST and other standards bodies. This will reduce the number of false positives for this query.
+* The first argument of the method `getInstance` of `java.security.Signature` is now modeled as a sink for `java/potentially-weak-cryptographic-algorithm`, `java/weak-cryptographic-algorithm` and `java/rsa-without-oaep`. This will increase the number of alerts for these queries.
+* Kotlin versions up to 2.3.20 are now supported.
+
## 9.0.2
No user-facing changes.
diff --git a/java/ql/lib/change-notes/2026-03-20-data-extensions-barriers.md b/java/ql/lib/change-notes/2026-03-20-data-extensions-barriers.md
new file mode 100644
index 00000000000..f8bcbb1fcb2
--- /dev/null
+++ b/java/ql/lib/change-notes/2026-03-20-data-extensions-barriers.md
@@ -0,0 +1,4 @@
+---
+category: feature
+---
+* Data flow barriers and barrier guards can now be added using data extensions. For more information see [Customizing library models for Java and Kotlin](https://codeql.github.com/docs/codeql-language-guides/customizing-library-models-for-java-and-kotlin/).
diff --git a/java/ql/lib/change-notes/2026-03-26-kotlin-2.3.20.md b/java/ql/lib/change-notes/2026-03-26-kotlin-2.3.20.md
deleted file mode 100644
index b18cff45704..00000000000
--- a/java/ql/lib/change-notes/2026-03-26-kotlin-2.3.20.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: minorAnalysis
----
-* Kotlin versions up to 2.3.20 are now supported.
diff --git a/java/ql/lib/change-notes/2026-03-28-tainted-arithmetic-bounds-check.md b/java/ql/lib/change-notes/2026-03-28-tainted-arithmetic-bounds-check.md
deleted file mode 100644
index 0688815c822..00000000000
--- a/java/ql/lib/change-notes/2026-03-28-tainted-arithmetic-bounds-check.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: minorAnalysis
----
-* The `java/tainted-arithmetic` query no longer flags arithmetic expressions that are used directly as an operand of a comparison in `if`-condition bounds-checking patterns. For example, `if (off + len > array.length)` is now recognized as a bounds check rather than a potentially vulnerable computation, reducing false positives.
diff --git a/java/ql/lib/change-notes/2026-04-04-sensitive-log-fp-reduction.md b/java/ql/lib/change-notes/2026-04-04-sensitive-log-fp-reduction.md
new file mode 100644
index 00000000000..15fc811360b
--- /dev/null
+++ b/java/ql/lib/change-notes/2026-04-04-sensitive-log-fp-reduction.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* The `java/sensitive-log` query now excludes additional common variable naming patterns that do not hold sensitive data, reducing false positives. This includes pagination/iteration tokens (`nextToken`, `pageToken`, `continuationToken`), token metadata (`tokenType`, `tokenEndpoint`, `tokenCount`), and secret metadata (`secretName`, `secretId`, `secretVersion`).
diff --git a/java/ql/lib/change-notes/2026-04-18-partial-path-traversal-fix.md b/java/ql/lib/change-notes/2026-04-18-partial-path-traversal-fix.md
new file mode 100644
index 00000000000..8c15a346552
--- /dev/null
+++ b/java/ql/lib/change-notes/2026-04-18-partial-path-traversal-fix.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* The `java/partial-path-traversal` and `java/partial-path-traversal-from-remote` queries now correctly recognize file separator appends using `+=`.
diff --git a/java/ql/lib/change-notes/2026-03-27-add-ec-to-secure-algorithms.md b/java/ql/lib/change-notes/released/9.0.3.md
similarity index 63%
rename from java/ql/lib/change-notes/2026-03-27-add-ec-to-secure-algorithms.md
rename to java/ql/lib/change-notes/released/9.0.3.md
index ee53bedd417..828b5867f8b 100644
--- a/java/ql/lib/change-notes/2026-03-27-add-ec-to-secure-algorithms.md
+++ b/java/ql/lib/change-notes/released/9.0.3.md
@@ -1,5 +1,8 @@
----
-category: minorAnalysis
----
+## 9.0.3
+
+### Minor Analysis Improvements
+
+* The `java/tainted-arithmetic` query no longer flags arithmetic expressions that are used directly as an operand of a comparison in `if`-condition bounds-checking patterns. For example, `if (off + len > array.length)` is now recognized as a bounds check rather than a potentially vulnerable computation, reducing false positives.
* The `java/potentially-weak-cryptographic-algorithm` query no longer flags Elliptic Curve algorithms (`EC`, `ECDSA`, `ECDH`, `EdDSA`, `Ed25519`, `Ed448`, `XDH`, `X25519`, `X448`), HMAC-based algorithms (`HMACSHA1`, `HMACSHA256`, `HMACSHA384`, `HMACSHA512`), or PBKDF2 key derivation as potentially insecure. These are modern, secure algorithms recommended by NIST and other standards bodies. This will reduce the number of false positives for this query.
* The first argument of the method `getInstance` of `java.security.Signature` is now modeled as a sink for `java/potentially-weak-cryptographic-algorithm`, `java/weak-cryptographic-algorithm` and `java/rsa-without-oaep`. This will increase the number of alerts for these queries.
+* Kotlin versions up to 2.3.20 are now supported.
diff --git a/java/ql/lib/change-notes/released/9.0.4.md b/java/ql/lib/change-notes/released/9.0.4.md
new file mode 100644
index 00000000000..a5499634951
--- /dev/null
+++ b/java/ql/lib/change-notes/released/9.0.4.md
@@ -0,0 +1,5 @@
+## 9.0.4
+
+### Minor Analysis Improvements
+
+* The queries "Resolving XML external entity in user-controlled data" (`java/xxe`) and "Resolving XML external entity in user-controlled data from local source" (`java/xxe-local`) now recognize sinks in the Woodstox StAX library when `com.ctc.wstx.stax.WstxInputFactory` or `org.codehaus.stax2.XMLInputFactory2` are used directly.
diff --git a/java/ql/lib/codeql-pack.release.yml b/java/ql/lib/codeql-pack.release.yml
index 533351acdc9..4bbe4f75b58 100644
--- a/java/ql/lib/codeql-pack.release.yml
+++ b/java/ql/lib/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 9.0.2
+lastReleaseVersion: 9.0.4
diff --git a/java/ql/lib/qlpack.yml b/java/ql/lib/qlpack.yml
index fc1d2c13281..efa1d011ea5 100644
--- a/java/ql/lib/qlpack.yml
+++ b/java/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/java-all
-version: 9.0.3-dev
+version: 9.0.5-dev
groups: java
dbscheme: config/semmlecode.dbscheme
extractor: java
diff --git a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
index 1536c81aa08..a6a9347ca03 100644
--- a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
@@ -4,13 +4,17 @@
* Provides classes and predicates for dealing with flow models specified
* in data extensions and CSV format.
*
- * The CSV specification has the following columns:
+ * The extensible relations have the following columns:
* - Sources:
* `package; type; subtypes; name; signature; ext; output; kind; provenance`
* - Sinks:
* `package; type; subtypes; name; signature; ext; input; kind; provenance`
* - Summaries:
* `package; type; subtypes; name; signature; ext; input; output; kind; provenance`
+ * - Barriers:
+ * `package; type; subtypes; name; signature; ext; output; kind; provenance`
+ * - BarrierGuards:
+ * `package; type; subtypes; name; signature; ext; input; acceptingValue; kind; provenance`
* - Neutrals:
* `package; type; name; signature; kind; provenance`
* A neutral is used to indicate that a callable is neutral with respect to flow (no summary), source (is not a source) or sink (is not a sink).
@@ -69,14 +73,17 @@
* in the given range. The range is inclusive at both ends.
* - "ReturnValue": Selects the return value of a call to the selected element.
* - "Element": Selects the collection elements of the selected element.
- * 8. The `kind` column is a tag that can be referenced from QL to determine to
+ * 8. The `acceptingValue` column of barrier guard models specifies the condition
+ * under which the guard 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:
@@ -358,11 +365,11 @@ module ModelValidation {
result = "Unrecognized provenance description \"" + provenance + "\" in " + pred + " model."
)
or
- exists(string acceptingvalue |
- barrierGuardModel(_, _, _, _, _, _, _, acceptingvalue, _, _, _) and
- invalidAcceptingValue(acceptingvalue) and
+ exists(string acceptingValue |
+ barrierGuardModel(_, _, _, _, _, _, _, acceptingValue, _, _, _) and
+ invalidAcceptingValue(acceptingValue) and
result =
- "Unrecognized accepting value description \"" + acceptingvalue +
+ "Unrecognized accepting value description \"" + acceptingValue +
"\" in barrier guard model."
)
}
@@ -583,13 +590,13 @@ private module Cached {
private predicate barrierGuardChecks(Guard g, Expr e, GuardValue gv, TKindModelPair kmp) {
exists(
- SourceSinkInterpretationInput::InterpretNode n, AcceptingValue acceptingvalue, string kind,
+ SourceSinkInterpretationInput::InterpretNode n, AcceptingValue acceptingValue, string kind,
string model
|
- isBarrierGuardNode(n, acceptingvalue, kind, model) and
+ isBarrierGuardNode(n, acceptingValue, kind, model) and
n.asNode().asExpr() = e and
kmp = TMkPair(kind, model) and
- gv = convertAcceptingValue(acceptingvalue)
+ gv = convertAcceptingValue(acceptingValue)
|
g.(Call).getAnArgument() = e or g.(MethodCall).getQualifier() = e
)
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/ExternalFlowExtensions.qll b/java/ql/lib/semmle/code/java/dataflow/internal/ExternalFlowExtensions.qll
index be474ad4535..3c6b003876d 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/ExternalFlowExtensions.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/ExternalFlowExtensions.qll
@@ -35,7 +35,7 @@ extensible predicate barrierModel(
*/
extensible predicate barrierGuardModel(
string package, string type, boolean subtypes, string name, string signature, string ext,
- string input, string acceptingvalue, string kind, string provenance, QlBuiltins::ExtensionId madId
+ string input, string acceptingValue, string kind, string provenance, QlBuiltins::ExtensionId madId
);
/**
diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll b/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll
index 64fa30c7d91..453b7ccae11 100644
--- a/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll
@@ -282,7 +282,7 @@ module SourceSinkInterpretationInput implements
}
predicate barrierGuardElement(
- Element e, string input, Public::AcceptingValue acceptingvalue, string kind,
+ Element e, string input, Public::AcceptingValue acceptingValue, string kind,
Public::Provenance provenance, string model
) {
exists(
@@ -290,7 +290,7 @@ module SourceSinkInterpretationInput implements
SourceOrSinkElement baseBarrier, string originalInput
|
barrierGuardModel(namespace, type, subtypes, name, signature, ext, originalInput,
- acceptingvalue, kind, provenance, model) and
+ acceptingValue, kind, provenance, model) and
baseBarrier = interpretElement(namespace, type, subtypes, name, signature, ext, _) and
(
e = baseBarrier and input = originalInput
diff --git a/java/ql/lib/semmle/code/java/security/PartialPathTraversal.qll b/java/ql/lib/semmle/code/java/security/PartialPathTraversal.qll
index 63ffb62ef63..a7b0c50b082 100644
--- a/java/ql/lib/semmle/code/java/security/PartialPathTraversal.qll
+++ b/java/ql/lib/semmle/code/java/security/PartialPathTraversal.qll
@@ -40,8 +40,11 @@ private class CharacterLiteralFileSeparatorExpr extends FileSeparatorExpr, Chara
CharacterLiteralFileSeparatorExpr() { this.getValue() = "/" or this.getValue() = "\\" }
}
-private class FileSeparatorAppend extends AddExpr {
- FileSeparatorAppend() { this.getRightOperand() instanceof FileSeparatorExpr }
+private class FileSeparatorAppend extends BinaryExpr {
+ FileSeparatorAppend() {
+ this.(AddExpr).getRightOperand() instanceof FileSeparatorExpr or
+ this.(AssignAddExpr).getRightOperand() instanceof FileSeparatorExpr
+ }
}
private predicate isSafe(Expr expr) {
diff --git a/java/ql/lib/semmle/code/java/security/SensitiveActions.qll b/java/ql/lib/semmle/code/java/security/SensitiveActions.qll
index 6733219a8d5..86ff96c1917 100644
--- a/java/ql/lib/semmle/code/java/security/SensitiveActions.qll
+++ b/java/ql/lib/semmle/code/java/security/SensitiveActions.qll
@@ -40,14 +40,26 @@ string getCommonSensitiveInfoRegex() {
/**
* Gets a regular expression for matching common names of variables that
- * indicate the value being held does not contains sensitive information,
+ * indicate the value being held does not contain sensitive information,
* but is a false positive for `getCommonSensitiveInfoRegex`.
*
* - "tokenizer" is often used for java.util.StringTokenizer.
* - "tokenImage" appears in parser code generated by JavaCC.
+ * - Pagination/iteration tokens: "nextToken" (AWS SDK), "pageToken" (GCP), etc.
+ * - Token metadata: "tokenType" (OAuth), "tokenEndpoint" (OIDC), "tokenCount", etc.
+ * - Secret metadata: "secretName" (K8s/AWS), "secretId" (Azure), "secretVersion", etc.
*/
string getCommonSensitiveInfoFPRegex() {
- result = "(?i).*(null|tokenizer).*" or result = "tokenImage"
+ result =
+ [
+ "(?i).*(null|tokenizer).*", "tokenImage",
+ // Pagination/iteration tokens (e.g., AWS SDK pagination cursors, parser tokens)
+ "(?i).*(next|previous|current|page|continuation|cursor)tokens?.*",
+ // Token metadata/infrastructure (token followed by a non-value descriptor)
+ "(?i).*tokens?(type|kind|count|index|position|length|offset|endpoint|url|uri|bucket|rate|delimiter|separator|format|number|name|id|prefix|suffix|pattern|class|style).*",
+ // Secret metadata (secret followed by a non-value descriptor)
+ "(?i).*secrets?(name|id|version|ref|arn|path|type|label|description|manager|client|provider|store|factory|properties).*"
+ ]
}
/** An expression that might contain sensitive data. */
diff --git a/java/ql/lib/semmle/code/java/security/XmlParsers.qll b/java/ql/lib/semmle/code/java/security/XmlParsers.qll
index bd1520034eb..602076996a7 100644
--- a/java/ql/lib/semmle/code/java/security/XmlParsers.qll
+++ b/java/ql/lib/semmle/code/java/security/XmlParsers.qll
@@ -179,12 +179,29 @@ class XmlInputFactory extends RefType {
XmlInputFactory() { this.hasQualifiedName(javaxOrJakarta() + ".xml.stream", "XMLInputFactory") }
}
-/** A call to `XMLInputFactory.createXMLStreamReader`. */
+/**
+ * The class `com.ctc.wstx.stax.WstxInputFactory` or its abstract supertype
+ * `org.codehaus.stax2.XMLInputFactory2` from the Woodstox StAX library.
+ */
+class WstxInputFactory extends RefType {
+ WstxInputFactory() {
+ this.hasQualifiedName("com.ctc.wstx.stax", "WstxInputFactory") or
+ this.hasQualifiedName("org.codehaus.stax2", "XMLInputFactory2")
+ }
+}
+
+/**
+ * A call to `XMLInputFactory.createXMLStreamReader` or the equivalent method on the
+ * Woodstox `WstxInputFactory`.
+ */
class XmlInputFactoryStreamReader extends XmlParserCall {
XmlInputFactoryStreamReader() {
exists(Method m |
this.getMethod() = m and
- m.getDeclaringType() instanceof XmlInputFactory and
+ (
+ m.getDeclaringType() instanceof XmlInputFactory or
+ m.getDeclaringType() instanceof WstxInputFactory
+ ) and
m.hasName("createXMLStreamReader")
)
}
@@ -212,7 +229,10 @@ class XmlInputFactoryEventReader extends XmlParserCall {
XmlInputFactoryEventReader() {
exists(Method m |
this.getMethod() = m and
- m.getDeclaringType() instanceof XmlInputFactory and
+ (
+ m.getDeclaringType() instanceof XmlInputFactory or
+ m.getDeclaringType() instanceof WstxInputFactory
+ ) and
m.hasName("createXMLEventReader")
)
}
@@ -235,7 +255,10 @@ class XmlInputFactoryConfig extends ParserConfig {
XmlInputFactoryConfig() {
exists(Method m |
m = this.getMethod() and
- m.getDeclaringType() instanceof XmlInputFactory and
+ (
+ m.getDeclaringType() instanceof XmlInputFactory or
+ m.getDeclaringType() instanceof WstxInputFactory
+ ) and
m.hasName("setProperty")
)
}
diff --git a/java/ql/src/CHANGELOG.md b/java/ql/src/CHANGELOG.md
index 8d9ce4a75b4..1b5d2bdad8a 100644
--- a/java/ql/src/CHANGELOG.md
+++ b/java/ql/src/CHANGELOG.md
@@ -1,3 +1,14 @@
+## 1.11.1
+
+No user-facing changes.
+
+## 1.11.0
+
+### Query Metadata Changes
+
+* The `@security-severity` metadata of `java/log-injection` has been reduced from 7.8 (high) to 6.1 (medium).
+* The `@security-severity` metadata of `java/android/webview-addjavascriptinterface`, `java/android/websettings-javascript-enabled` and `java/xss` has been increased from 6.1 (medium) to 7.8 (high).
+
## 1.10.11
No user-facing changes.
diff --git a/java/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md b/java/ql/src/change-notes/released/1.11.0.md
similarity index 88%
rename from java/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md
rename to java/ql/src/change-notes/released/1.11.0.md
index fa1288af16e..0be4b0481d6 100644
--- a/java/ql/src/change-notes/2026-03-13-adjust-xss-and-log-injection-severity.md
+++ b/java/ql/src/change-notes/released/1.11.0.md
@@ -1,5 +1,6 @@
----
-category: queryMetadata
----
+## 1.11.0
+
+### Query Metadata Changes
+
* The `@security-severity` metadata of `java/log-injection` has been reduced from 7.8 (high) to 6.1 (medium).
* The `@security-severity` metadata of `java/android/webview-addjavascriptinterface`, `java/android/websettings-javascript-enabled` and `java/xss` has been increased from 6.1 (medium) to 7.8 (high).
diff --git a/java/ql/src/change-notes/released/1.11.1.md b/java/ql/src/change-notes/released/1.11.1.md
new file mode 100644
index 00000000000..f5047685223
--- /dev/null
+++ b/java/ql/src/change-notes/released/1.11.1.md
@@ -0,0 +1,3 @@
+## 1.11.1
+
+No user-facing changes.
diff --git a/java/ql/src/codeql-pack.release.yml b/java/ql/src/codeql-pack.release.yml
index d267e07fb66..4ae123153bf 100644
--- a/java/ql/src/codeql-pack.release.yml
+++ b/java/ql/src/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 1.10.11
+lastReleaseVersion: 1.11.1
diff --git a/java/ql/src/qlpack.yml b/java/ql/src/qlpack.yml
index 78372a6ec85..2f2233460ba 100644
--- a/java/ql/src/qlpack.yml
+++ b/java/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/java-queries
-version: 1.10.12-dev
+version: 1.11.2-dev
groups:
- java
- queries
diff --git a/java/ql/test-kotlin1/library-tests/java-kotlin-collection-type-generic-methods/test.expected b/java/ql/test-kotlin1/library-tests/java-kotlin-collection-type-generic-methods/test.expected
index a56980d10ac..a55e73e283f 100644
--- a/java/ql/test-kotlin1/library-tests/java-kotlin-collection-type-generic-methods/test.expected
+++ b/java/ql/test-kotlin1/library-tests/java-kotlin-collection-type-generic-methods/test.expected
@@ -196,6 +196,8 @@ methodWithDuplicate
| List | listIterator | int |
| List | of | E |
| List | of | E[] |
+| List | ofLazy | IntFunction extends E> |
+| List | ofLazy | int |
| List | remove | Object |
| List | remove | int |
| List | removeAll | Collection> |
@@ -222,6 +224,8 @@ methodWithDuplicate
| List | listIterator | int |
| List | of | E |
| List | of | E[] |
+| List | ofLazy | IntFunction extends E> |
+| List | ofLazy | int |
| List | remove | Object |
| List | remove | int |
| List | removeAll | Collection> |
@@ -248,6 +252,8 @@ methodWithDuplicate
| List | listIterator | int |
| List | of | E |
| List | of | E[] |
+| List | ofLazy | IntFunction extends E> |
+| List | ofLazy | int |
| List | remove | Object |
| List | remove | int |
| List | removeAll | Collection> |
@@ -280,6 +286,8 @@ methodWithDuplicate
| Map | of | K |
| Map | of | V |
| Map | ofEntries | Entry extends K,? extends V>[] |
+| Map | ofLazy | Function super K,? extends V> |
+| Map | ofLazy | Set extends K> |
| Map | put | K |
| Map | put | V |
| Map | putAll | Map extends K,? extends V> |
@@ -310,6 +318,8 @@ methodWithDuplicate
| Map | of | K |
| Map | of | V |
| Map | ofEntries | Entry extends K,? extends V>[] |
+| Map | ofLazy | Function super K,? extends V> |
+| Map | ofLazy | Set extends K> |
| Map | put | Identity |
| Map | put | Object |
| Map | putAll | Map extends Identity,? extends Object> |
@@ -341,6 +351,8 @@ methodWithDuplicate
| Map | of | K |
| Map | of | V |
| Map | ofEntries | Entry extends K,? extends V>[] |
+| Map | ofLazy | Function super K,? extends V> |
+| Map | ofLazy | Set extends K> |
| Map | put | K |
| Map | put | V |
| Map | putAll | Map extends K,? extends V> |
@@ -370,6 +382,8 @@ methodWithDuplicate
| Map