Compare commits

..

7 Commits

Author SHA1 Message Date
Taus
ca59ca0c2f Python: Add globallyDefinedName and extend monkeyPatchedBuiltin 2026-04-08 15:58:39 +00:00
Taus
205466d7ab Python: Model undefinedness
Adds `maybeUndefined` to the reachability module, modelling which
names/variables may be undefined at runtime. The approach is very close
to the one used in points-to, though it of course relies on our new
modelling of exceptions/reachability instead.
2026-04-08 15:58:39 +00:00
Taus
3e7986a14a Python: Extend reachability analysis with common guards
Adds `if False: ...` and `if typing.TYPE_CHECKING: ...` to the set of
nodes that are unlikely to be reachable.
2026-04-08 15:58:38 +00:00
Taus
ec9e72ee09 Python: Add getClassName for immutable literals
Used for queries where we mention the class of a literal in the alert
message.
2026-04-08 15:58:38 +00:00
Taus
6efedb7d00 Python: Extend ExceptionTypes API
Adds support for finding instances, and adds things like a
`BaseException` convenience class.
2026-04-08 15:58:38 +00:00
Taus
993311e436 Python: Add Reachability module
The implementation is essentially the same as the one from
`BasicBlockWithPointsTo`, with the main difference being that this one
uses the exception machinery we just added (and some extensions added in
this commit).
2026-04-08 15:54:48 +00:00
Taus
e14d493bcc Python: Move exception modelling to DataFlowDispatch.qll 2026-04-08 12:18:56 +00:00
33 changed files with 516 additions and 358 deletions

View File

@@ -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.50.0")
bazel_dep(name = "gazelle", version = "0.47.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")

View File

@@ -26,23 +26,10 @@ 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 jobHasPermissions(job) and
not exists(job.getPermissions()) and
not exists(job.getEnclosingWorkflow().getPermissions()) and
// exists a trigger event that is not a workflow_call
exists(Event e |
e = job.getATriggerEvent() and

View File

@@ -20,6 +20,6 @@ from ArtifactPoisoningFlow::PathNode source, ArtifactPoisoningFlow::PathNode sin
where
ArtifactPoisoningFlow::flowPath(source, sink) and
event = getRelevantEventInPrivilegedContext(sink.getNode())
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()
select sink.getNode(), source, sink,
"Potential artifact poisoning in $@, which may be controlled by an external user ($@).", sink,
sink.getNode().toString(), event, event.getName()

View File

@@ -20,5 +20,6 @@ from ArtifactPoisoningFlow::PathNode source, ArtifactPoisoningFlow::PathNode sin
where
ArtifactPoisoningFlow::flowPath(source, sink) and
inNonPrivilegedContext(sink.getNode().asExpr())
select source.getNode(), source, sink,
"Potential artifact poisoning; the artifact being consumed has contents that may be controlled by an external user."
select sink.getNode(), source, sink,
"Potential artifact poisoning in $@, which may be controlled by an external user.", sink,
sink.getNode().toString()

View File

@@ -1,4 +0,0 @@
---
category: majorAnalysis
---
* 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 clarify the wording to make it clear that it is not the artifact that is being poisoned, but instead a potentially untrusted artifact that is consumed. Also change the alert location to be the source, to align more with other queries reporting an artifact (e.g. zipslip) which is more useful.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* The query `actions/missing-workflow-permissions` no longer produces false positive results on reusable workflows where all callers set permissions.

View File

@@ -1,9 +0,0 @@
on:
workflow_call:
jobs:
build:
name: Build and test
runs-on: ubuntu-latest
steps:
- uses: actions/deploy-pages

View File

@@ -1,11 +0,0 @@
on:
workflow_dispatch:
permissions:
contents: read
id-token: write
pages: write
jobs:
call-workflow:
uses: ./.github/workflows/perms11.yml

View File

@@ -55,21 +55,21 @@ nodes
| .github/workflows/test25.yml:39:14:40:45 | ./gradlew buildScanPublishPrevious\n | semmle.label | ./gradlew buildScanPublishPrevious\n |
subpaths
#select
| .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 |
| .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 |

View File

@@ -7,12 +7,10 @@ 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
@@ -30,7 +28,6 @@ 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

View File

@@ -5,7 +5,7 @@
* @kind problem
* @problem.severity warning
* @security-severity 8.1
* @precision high
* @precision medium
* @id cpp/integer-multiplication-cast-to-long
* @tags reliability
* security

View File

@@ -5,7 +5,7 @@
* @kind problem
* @problem.severity error
* @security-severity 7.5
* @precision high
* @precision medium
* @id cpp/wrong-type-format-argument
* @tags reliability
* correctness

View File

@@ -14,9 +14,6 @@ function may behave unpredictably.</p>
<p>This may indicate a misspelled function name, or that the required header containing
the function declaration has not been included.</p>
<p>Note: This query is not compatible with <code>build mode: none</code> databases, and produces
no results on those databases.</p>
</overview>
<recommendation>
<p>Provide an explicit declaration of the function before invoking it.</p>
@@ -29,4 +26,4 @@ no results on those databases.</p>
<references>
<li>SEI CERT C Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/c/DCL31-C.+Declare+identifiers+before+using+them">DCL31-C. Declare identifiers before using them</a></li>
</references>
</qhelp>
</qhelp>

View File

@@ -5,7 +5,7 @@
* may lead to unpredictable behavior.
* @kind problem
* @problem.severity warning
* @precision high
* @precision medium
* @id cpp/implicit-function-declaration
* @tags correctness
* maintainability
@@ -17,11 +17,6 @@ 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
@@ -44,7 +39,6 @@ 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

View File

@@ -6,7 +6,7 @@
* @kind problem
* @problem.severity warning
* @security-severity 7.8
* @precision high
* @precision medium
* @tags reliability
* security
* external/cwe/cwe-190

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* The "Implicit function declaration" (`cpp/implicit-function-declaration`) query no longer produces results on `build mode: none` databases. These results were found to be very noisy and fundamentally imprecise in this mode.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* The "Comparison of narrow type with wide type in loop condition" (`cpp/comparison-with-wider-type`) query has been upgraded to `high` precision. This query will now run in the default code scanning suite.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* The "Implicit function declaration" (`cpp/implicit-function-declaration`) query has been upgraded to `high` precision.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* The "Multiplication result converted to larger type" (`cpp/integer-multiplication-cast-to-long`) query has been upgraded to `high` precision. This query will now run in the default code scanning suite.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* The "Wrong type of arguments to formatting function" (`cpp/wrong-type-format-argument`) query has been upgraded to `high` precision. This query will now run in the default code scanning suite.

View File

@@ -232,9 +232,14 @@ private module Identity {
*/
pragma[nomagic]
private predicate convTypeArguments(Type fromTypeArgument, Type toTypeArgument, int i) {
fromTypeArgument = getTypeArgumentRanked(_, _, pragma[only_bind_into](i)) and
toTypeArgument = getTypeArgumentRanked(_, _, pragma[only_bind_into](i)) and
convIdentity(fromTypeArgument, toTypeArgument)
exists(int j |
fromTypeArgument = getTypeArgumentRanked(_, _, i) and
toTypeArgument = getTypeArgumentRanked(_, _, j) and
i <= j and
j <= i
|
convIdentity(fromTypeArgument, toTypeArgument)
)
}
pragma[nomagic]
@@ -924,16 +929,19 @@ private module Variance {
private predicate convTypeArguments(
TypeArgument fromTypeArgument, TypeArgument toTypeArgument, int i, TVariance v
) {
fromTypeArgument = getTypeArgumentRanked(_, _, pragma[only_bind_into](i), _) and
toTypeArgument = getTypeArgumentRanked(_, _, pragma[only_bind_into](i), _) and
(
exists(int j |
fromTypeArgument = getTypeArgumentRanked(_, _, i, _) and
toTypeArgument = getTypeArgumentRanked(_, _, j, _) and
i <= j and
j <= i
|
convIdentity(fromTypeArgument, toTypeArgument) and
v = TNone()
or
convRefTypeTypeArgumentOut(fromTypeArgument, toTypeArgument, i) and
convRefTypeTypeArgumentOut(fromTypeArgument, toTypeArgument, j) and
v = TOut()
or
convRefTypeTypeArgumentIn(toTypeArgument, fromTypeArgument, i) and
convRefTypeTypeArgumentIn(toTypeArgument, fromTypeArgument, j) and
v = TIn()
)
}

View File

@@ -29,8 +29,4 @@ module CsharpDataFlow implements InputSig<Location> {
predicate neverSkipInPathGraph(Node n) {
exists(n.(AssignableDefinitionNode).getDefinition().getTargetAccess())
}
DataFlowType getSourceContextParameterNodeType(Node p) {
exists(p) and result.isSourceContextParameterType()
}
}

View File

@@ -1179,8 +1179,7 @@ private module Cached {
cached
newtype TDataFlowType =
TGvnDataFlowType(Gvn::GvnType t) or
TDelegateDataFlowType(Callable lambda) { lambdaCreationExpr(_, lambda) } or
TSourceContextParameterType()
TDelegateDataFlowType(Callable lambda) { lambdaCreationExpr(_, lambda) }
}
import Cached
@@ -2395,8 +2394,6 @@ class DataFlowType extends TDataFlowType {
Callable asDelegate() { this = TDelegateDataFlowType(result) }
predicate isSourceContextParameterType() { this = TSourceContextParameterType() }
/**
* Gets an expression that creates a delegate of this type.
*
@@ -2415,9 +2412,6 @@ class DataFlowType extends TDataFlowType {
result = this.asGvnType().toString()
or
result = this.asDelegate().toString()
or
this.isSourceContextParameterType() and
result = "<source context parameter type>"
}
}
@@ -2475,11 +2469,6 @@ 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`.
@@ -2510,10 +2499,6 @@ predicate compatibleTypes(DataFlowType dt1, DataFlowType dt2) {
compatibleTypesDelegateLeft(dt2, dt1)
or
dt1.asDelegate() = dt2.asDelegate()
or
compatibleTypesSourceContextParameterTypeLeft(dt1, dt2)
or
compatibleTypesSourceContextParameterTypeLeft(dt2, dt1)
}
pragma[nomagic]
@@ -2526,8 +2511,6 @@ predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) {
uselessTypebound(t2)
or
compatibleTypesDelegateLeft(t1, t2)
or
compatibleTypesSourceContextParameterTypeLeft(t1, t2)
}
/**

View File

@@ -536,12 +536,6 @@ 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

View File

@@ -11,23 +11,23 @@
Microsoft extensions (up to VS 2022),
Arm Compiler 5 [5]_","``.cpp``, ``.c++``, ``.cxx``, ``.hpp``, ``.hh``, ``.h++``, ``.hxx``, ``.c``, ``.cc``, ``.h``"
C#,C# up to 14 [6]_,"Microsoft Visual Studio up to 2019 with .NET up to 4.8,
C#,C# up to 13,"Microsoft Visual Studio up to 2019 with .NET up to 4.8,
.NET Core up to 3.1
.NET 5, .NET 6, .NET 7, .NET 8, .NET 9, .NET 10 [6]_","``.sln``, ``.slnx``, ``.csproj``, ``.cs``, ``.cshtml``, ``.xaml``"
.NET 5, .NET 6, .NET 7, .NET 8, .NET 9","``.sln``, ``.slnx``, ``.csproj``, ``.cs``, ``.cshtml``, ``.xaml``"
GitHub Actions,"Not applicable",Not applicable,"``.github/workflows/*.yml``, ``.github/workflows/*.yaml``, ``**/action.yml``, ``**/action.yaml``"
Go (aka Golang), "Go up to 1.26", "Go 1.11 or more recent", ``.go``
Java,"Java 7 to 26 [7]_","javac (OpenJDK and Oracle JDK),
Java,"Java 7 to 26 [6]_","javac (OpenJDK and Oracle JDK),
Eclipse compiler for Java (ECJ) [8]_",``.java``
Eclipse compiler for Java (ECJ) [7]_",``.java``
Kotlin,"Kotlin 1.8.0 to 2.3.2\ *x*","kotlinc",``.kt``
JavaScript,ECMAScript 2022 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhtm``, ``.xhtml``, ``.vue``, ``.hbs``, ``.ejs``, ``.njk``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [9]_"
Python [10]_,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13",Not applicable,``.py``
Ruby [11]_,"up to 3.3",Not applicable,"``.rb``, ``.erb``, ``.gemspec``, ``Gemfile``"
Rust [12]_,"Rust editions 2021 and 2024","Rust compiler","``.rs``, ``Cargo.toml``"
Swift [13]_ [14]_,"Swift 5.4-6.2","Swift compiler","``.swift``"
TypeScript [15]_,"2.6-5.9",Standard TypeScript compiler,"``.ts``, ``.tsx``, ``.mts``, ``.cts``"
JavaScript,ECMAScript 2022 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhtm``, ``.xhtml``, ``.vue``, ``.hbs``, ``.ejs``, ``.njk``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [8]_"
Python [9]_,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13",Not applicable,``.py``
Ruby [10]_,"up to 3.3",Not applicable,"``.rb``, ``.erb``, ``.gemspec``, ``Gemfile``"
Rust [11]_,"Rust editions 2021 and 2024","Rust compiler","``.rs``, ``Cargo.toml``"
Swift [12]_ [13]_,"Swift 5.4-6.2","Swift compiler","``.swift``"
TypeScript [14]_,"2.6-5.9",Standard TypeScript compiler,"``.ts``, ``.tsx``, ``.mts``, ``.cts``"
.. container:: footnote-group
@@ -36,13 +36,12 @@
.. [3] Objective-C, Objective-C++, C++/CLI, and C++/CX are not supported.
.. [4] Support for the clang-cl compiler is preliminary.
.. [5] Support for the Arm Compiler (armcc) is preliminary.
.. [6] Support for .NET 10 is preliminary and code that uses language features new to C# 14 is not yet fully supported for extraction and analysis.
.. [7] Builds that execute on Java 7 to 26 can be analyzed. The analysis understands standard language features in Java 8 to 26; "preview" and "incubator" features are not supported. Source code using Java language versions older than Java 8 are analyzed as Java 8 code.
.. [8] ECJ is supported when the build invokes it via the Maven Compiler plugin or the Takari Lifecycle plugin.
.. [9] JSX and Flow code, YAML, JSON, HTML, and XML files may also be analyzed with JavaScript files.
.. [10] The extractor requires Python 3 to run. To analyze Python 2.7 you should install both versions of Python.
.. [11] Requires glibc 2.17.
.. [12] Requires ``rustup`` and ``cargo`` to be installed. Features from nightly toolchains are not supported.
.. [13] Support for the analysis of Swift requires macOS.
.. [14] Embedded Swift is not supported.
.. [15] TypeScript analysis is performed by running the JavaScript extractor with TypeScript enabled. This is the default.
.. [6] Builds that execute on Java 7 to 26 can be analyzed. The analysis understands standard language features in Java 8 to 26; "preview" and "incubator" features are not supported. Source code using Java language versions older than Java 8 are analyzed as Java 8 code.
.. [7] ECJ is supported when the build invokes it via the Maven Compiler plugin or the Takari Lifecycle plugin.
.. [8] JSX and Flow code, YAML, JSON, HTML, and XML files may also be analyzed with JavaScript files.
.. [9] The extractor requires Python 3 to run. To analyze Python 2.7 you should install both versions of Python.
.. [10] Requires glibc 2.17.
.. [11] Requires ``rustup`` and ``cargo`` to be installed. Features from nightly toolchains are not supported.
.. [12] Support for the analysis of Swift requires macOS.
.. [13] Embedded Swift is not supported.
.. [14] TypeScript analysis is performed by running the JavaScript extractor with TypeScript enabled. This is the default.

View File

@@ -26,7 +26,7 @@
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.25.4</version>
<version>2.14.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>

View File

@@ -1988,6 +1988,38 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { call = result.getCall
module DuckTyping {
private import semmle.python.ApiGraphs
/**
* Holds if `name` is a globally defined name (a builtin or VM-defined name).
*/
predicate globallyDefinedName(string name) {
exists(API::builtin(name))
or
name = "WindowsError"
or
name = "_" and exists(Module m | m.getName() = "gettext")
or
name in ["__file__", "__builtins__", "__name__"]
}
/**
* Holds if `name` is monkey-patched into the builtins module.
*/
predicate monkeyPatchedBuiltin(string name) {
any(DataFlow::AttrWrite aw)
.writes(API::moduleImport("builtins").getAValueReachableFromSource(), name, _)
or
// B.__dict__["name"] = value
exists(SubscriptNode subscr |
subscr.isStore() and
subscr.getObject() =
API::moduleImport("builtins")
.getMember("__dict__")
.getAValueReachableFromSource()
.asCfgNode() and
subscr.getIndex().getNode().(StringLiteral).getText() = name
)
}
/**
* Holds if `cls` or any of its resolved superclasses declares a method with the given `name`.
*/
@@ -2158,4 +2190,415 @@ module DuckTyping {
or
f.getADecorator().(Name).getId() = "property"
}
/** Gets the name of the builtin class of the immutable literal `lit`. */
string getClassName(ImmutableLiteral lit) {
lit instanceof IntegerLiteral and result = "int"
or
lit instanceof FloatLiteral and result = "float"
or
lit instanceof ImaginaryLiteral and result = "complex"
or
lit instanceof NegativeIntegerLiteral and result = "int"
or
lit instanceof StringLiteral and result = "str"
or
lit instanceof BooleanLiteral and result = "bool"
or
lit instanceof None and result = "NoneType"
}
}
/**
* Provides a class hierarchy for exception types, covering both builtin
* exceptions (from typeshed models) and user-defined exception classes.
*/
module ExceptionTypes {
private import semmle.python.ApiGraphs
private import semmle.python.frameworks.data.internal.ApiGraphModels
/** Holds if `name` is a builtin exception class name. */
predicate builtinException(string name) {
typeModel("builtins.BaseException~Subclass", "builtins." + name, "")
}
/** Holds if builtin exception `sub` is a direct subclass of builtin exception `base`. */
private predicate builtinExceptionSubclass(string base, string sub) {
typeModel("builtins." + base + "~Subclass", "builtins." + sub, "")
}
/** An exception type, either a builtin exception or a user-defined exception class. */
newtype TExceptType =
/** A user-defined exception class. */
TUserExceptType(Class c) or
/** A builtin exception class, identified by name. */
TBuiltinExceptType(string name) { builtinException(name) }
/** An exception type, either a builtin exception or a user-defined exception class. */
class ExceptType extends TExceptType {
/** Gets the name of this exception type. */
string getName() { none() }
/** Gets a data-flow node that refers to this exception type. */
DataFlow::Node getAUse() { none() }
/** Gets a direct superclass of this exception type. */
ExceptType getADirectSuperclass() { none() }
/** Gets a string representation of this exception type. */
string toString() { result = this.getName() }
/** Gets a data-flow node that refers to an instance of this exception type. */
DataFlow::Node getAnInstance() { none() }
/** Holds if this is a legal exception type (a subclass of `BaseException`). */
predicate isLegalExceptionType() { this.getADirectSuperclass*() instanceof BaseException }
/**
* Holds if this exception type is raised by `r`, either as a class reference
* (e.g. `raise ValueError`) or as an instantiation (e.g. `raise ValueError("msg")`).
*/
predicate isRaisedBy(Raise r) {
exists(Expr raised | raised = r.getRaised() |
this.getAUse().asExpr() in [raised, raised.(Call).getFunc()]
or
this.getAnInstance().asExpr() = raised
)
}
/** Holds if this exception type may be raised at control flow node `r`. */
predicate isRaisedAt(ControlFlowNode r) {
this.isRaisedBy(r.getNode())
or
exists(Function callee |
resolveCall(r, callee, _) and
this.isRaisedIn(callee)
)
}
/**
* Holds if this exception type may be raised in function `f`, either
* directly via `raise` statements or transitively through calls to other functions.
*/
predicate isRaisedIn(Function f) { this.isRaisedAt(any(ControlFlowNode r | r.getScope() = f)) }
/** Holds if this exception type is handled by the `except` clause at `handler`. */
predicate isHandledAt(ExceptFlowNode handler) {
exists(ExceptStmt ex, Expr typeExpr | ex = handler.getNode() |
(
typeExpr = ex.getType()
or
typeExpr = ex.getType().(Tuple).getAnElt()
) and
this.getAUse().asExpr() = typeExpr
)
or
// A bare `except:` handles everything
not exists(handler.getNode().(ExceptStmt).getType()) and
this instanceof BaseException
}
/**
* Holds if this element is at the specified location.
* The location spans column `startColumn` of line `startLine` to
* column `endColumn` of line `endLine` in file `filepath`.
* For more information, see
* [Providing locations in CodeQL queries](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filePath, int startLine, int startColumn, int endLine, int endColumn
) {
none()
}
}
/** A user-defined exception class. */
class UserExceptType extends ExceptType, TUserExceptType {
Class cls;
UserExceptType() { this = TUserExceptType(cls) }
/** Gets the underlying class. */
Class asClass() { result = cls }
override string getName() { result = cls.getName() }
override DataFlow::Node getAUse() { result = classTracker(cls) }
override DataFlow::Node getAnInstance() { result = classInstanceTracker(cls) }
override ExceptType getADirectSuperclass() {
result.(UserExceptType).asClass() = getADirectSuperclass(cls)
or
result.(BuiltinExceptType).getAUse().asExpr() = cls.getABase()
}
override predicate hasLocationInfo(
string filePath, int startLine, int startColumn, int endLine, int endColumn
) {
cls.getLocation().hasLocationInfo(filePath, startLine, startColumn, endLine, endColumn)
}
}
/** A builtin exception class, identified by name. */
class BuiltinExceptType extends ExceptType, TBuiltinExceptType {
string name;
BuiltinExceptType() { this = TBuiltinExceptType(name) }
/** Gets the builtin name. */
string asBuiltinName() { result = name }
override string getName() { result = name }
override DataFlow::Node getAUse() { result = API::builtin(name).getAValueReachableFromSource() }
override DataFlow::Node getAnInstance() {
result = API::builtin(name).getAnInstance().getAValueReachableFromSource()
}
override ExceptType getADirectSuperclass() {
builtinExceptionSubclass(result.(BuiltinExceptType).asBuiltinName(), name) and
result != this
}
override predicate hasLocationInfo(
string filePath, int startLine, int startColumn, int endLine, int endColumn
) {
filePath = "" and
startLine = 0 and
startColumn = 0 and
endLine = 0 and
endColumn = 0
}
}
/** The builtin `BaseException` type. */
class BaseException extends BuiltinExceptType {
BaseException() { name = "BaseException" }
}
/** The builtin `NameError` exception type. */
class NameError extends BuiltinExceptType {
NameError() { name = "NameError" }
}
/**
* Holds if the exception edge from `r` to `handler` is unlikely because
* none of the exception types that `r` may raise are handled by `handler`.
*/
predicate unlikelyExceptionEdge(ControlFlowNode r, ExceptFlowNode handler) {
handler = r.getAnExceptionalSuccessor() and
// We can determine at least one raised type
exists(ExceptType t | t.isRaisedAt(r)) and
// But none of them are handled by this handler
not exists(ExceptType raised, ExceptType handled |
raised.isRaisedAt(r) and
handled.isHandledAt(handler) and
raised.getADirectSuperclass*() = handled
)
}
}
/**
* Provides predicates for reasoning about the reachability of control flow nodes
* and basic blocks.
*/
module Reachability {
private import semmle.python.ApiGraphs
import ExceptionTypes
/**
* Holds if `call` is a call to a function that is known to never return normally
* (e.g. `sys.exit()`, `os._exit()`, `os.abort()`).
*/
predicate isCallToNeverReturningFunction(CallNode call) {
// Known never-returning builtins/stdlib functions via API graphs
call = API::builtin("exit").getACall().asCfgNode()
or
call = API::builtin("quit").getACall().asCfgNode()
or
call = API::moduleImport("sys").getMember("exit").getACall().asCfgNode()
or
call = API::moduleImport("os").getMember("_exit").getACall().asCfgNode()
or
call = API::moduleImport("os").getMember("abort").getACall().asCfgNode()
or
// User-defined functions that only contain raise statements (no normal returns)
exists(Function target |
resolveCall(call, target, _) and
neverReturns(target)
)
}
/**
* Holds if function `f` never returns normally, because every normal exit
* is dominated by a call to a never-returning function or an unconditional raise.
*/
predicate neverReturns(Function f) {
exists(f.getANormalExit()) and
forall(BasicBlock exit | exit = f.getANormalExit().getBasicBlock() |
exists(BasicBlock raising |
raising.dominates(exit) and
(
isCallToNeverReturningFunction(raising.getLastNode())
or
raising.getLastNode().getNode() instanceof Raise
)
)
)
}
/**
* Holds if `node` is unlikely to raise an exception. This includes entry nodes
* and simple name lookups.
*/
private predicate unlikelyToRaise(ControlFlowNode node) {
exists(node.getAnExceptionalSuccessor()) and
(
node.getNode() instanceof Name
or
exists(Scope s | s.getEntryNode() = node)
)
}
/**
* Holds if it is highly unlikely for control to flow from `node` to `succ`.
*/
predicate unlikelySuccessor(ControlFlowNode node, ControlFlowNode succ) {
// Exceptional edge where the raised type doesn't match the handler
unlikelyExceptionEdge(node, succ)
or
// Normal successor of a never-returning call
isCallToNeverReturningFunction(node) and
succ = node.getASuccessor() and
not succ = node.getAnExceptionalSuccessor() and
not succ.getNode() instanceof Yield
or
// Exception edge from a node that is unlikely to raise
unlikelyToRaise(node) and
succ = node.getAnExceptionalSuccessor()
or
// True branch of `if False:` or `if TYPE_CHECKING:`
isAlwaysFalseGuard(node) and
succ = node.getATrueSuccessor()
}
/**
* Holds if `node` is a condition that is always `False` at runtime.
* This covers `if False:` and `if typing.TYPE_CHECKING:`.
*/
private predicate isAlwaysFalseGuard(ControlFlowNode node) {
node.getNode() instanceof False
or
node =
API::moduleImport("typing")
.getMember("TYPE_CHECKING")
.getAValueReachableFromSource()
.asCfgNode()
}
private predicate startBbLikelyReachable(BasicBlock b) {
exists(Scope s | s.getEntryNode() = b.getNode(_))
or
exists(BasicBlock pred |
pred = b.getAPredecessor() and
endBbLikelyReachable(pred) and
not unlikelySuccessor(pred.getLastNode(), b)
)
}
private predicate endBbLikelyReachable(BasicBlock b) {
startBbLikelyReachable(b) and
not exists(ControlFlowNode p, ControlFlowNode s |
unlikelySuccessor(p, s) and
p = b.getNode(_) and
s = b.getNode(_) and
not p = b.getLastNode()
)
}
/**
* Holds if basic block `b` is likely to be reachable from the entry of its
* enclosing scope.
*/
predicate likelyReachable(BasicBlock b) { startBbLikelyReachable(b) }
/**
* Holds if it is unlikely that `node` can be reached during execution.
*/
predicate unlikelyReachable(ControlFlowNode node) {
not startBbLikelyReachable(node.getBasicBlock())
or
exists(BasicBlock b |
startBbLikelyReachable(b) and
not endBbLikelyReachable(b) and
exists(ControlFlowNode p, int i, int j |
unlikelySuccessor(p, _) and
p = b.getNode(i) and
node = b.getNode(j) and
i < j
)
)
}
/**
* Holds if `var` is an SSA variable that is implicitly defined (a builtin,
* VM-defined name, or `__path__` in a package init).
*/
private predicate implicitlyDefined(SsaVariable var) {
not exists(var.getDefinition()) and
not py_ssa_phi(var, _) and
exists(GlobalVariable gv | var.getVariable() = gv |
DuckTyping::globallyDefinedName(gv.getId())
or
gv.getId() = "__path__" and gv.getScope().(Module).isPackageInit()
)
}
/**
* Gets a phi input of `var`, pruned of unlikely edges.
*/
private SsaVariable getAPrunedPhiInput(SsaVariable var) {
result = var.getAPhiInput() and
exists(BasicBlock incoming | incoming = var.getPredecessorBlockForPhiArgument(result) |
not unlikelySuccessor(incoming.getLastNode(), var.getDefinition().getBasicBlock().firstNode())
)
}
/**
* Gets a predecessor block for a phi node, pruned of unlikely edges.
*/
private BasicBlock getAPrunedPredecessorBlockForPhi(SsaVariable var) {
result = var.getAPredecessorBlockForPhi() and
not unlikelySuccessor(result.getLastNode(), var.getDefinition().getBasicBlock().firstNode())
}
/**
* Holds if the SSA variable `var` may be undefined at some use.
*/
private predicate ssaMaybeUndefined(SsaVariable var) {
// No definition, not a phi, not implicitly defined
not exists(var.getDefinition()) and not py_ssa_phi(var, _) and not implicitlyDefined(var)
or
// Defined by a deletion
var.getDefinition().isDelete()
or
// A phi input may be undefined
exists(SsaVariable input | input = getAPrunedPhiInput(var) | ssaMaybeUndefined(input))
or
// A phi predecessor has no dominating definition
exists(BasicBlock incoming |
likelyReachable(incoming) and
incoming = getAPrunedPredecessorBlockForPhi(var) and
not var.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming)
)
}
/**
* Holds if the name `u` may be undefined at its use.
*/
predicate maybeUndefined(Name u) {
exists(SsaVariable var | var.getAUse().getNode() = u | ssaMaybeUndefined(var))
}
}

View File

@@ -15,74 +15,7 @@
import python
import semmle.python.dataflow.new.internal.DataFlowDispatch
import semmle.python.ApiGraphs
import semmle.python.frameworks.data.internal.ApiGraphModels
predicate builtinException(string name) {
typeModel("builtins.BaseException~Subclass", "builtins." + name, "")
}
predicate builtinExceptionSubclass(string base, string sub) {
typeModel("builtins." + base + "~Subclass", "builtins." + sub, "")
}
newtype TExceptType =
TClass(Class c) or
TBuiltin(string name) { builtinException(name) }
class ExceptType extends TExceptType {
Class asClass() { this = TClass(result) }
string asBuiltinName() { this = TBuiltin(result) }
predicate isBuiltin() { this = TBuiltin(_) }
string getName() {
result = this.asClass().getName()
or
result = this.asBuiltinName()
}
string toString() { result = this.getName() }
DataFlow::Node getAUse() {
result = classTracker(this.asClass())
or
API::builtin(this.asBuiltinName()).asSource().flowsTo(result)
}
ExceptType getADirectSuperclass() {
result.asClass() = getADirectSuperclass(this.asClass())
or
result.isBuiltin() and
result.getAUse().asExpr() = this.asClass().getABase()
or
builtinExceptionSubclass(result.asBuiltinName(), this.asBuiltinName()) and
this != result
}
/**
* Holds if this element is at the specified location.
* The location spans column `startColumn` of line `startLine` to
* column `endColumn` of line `endLine` in file `filepath`.
* For more information, see
* [Providing locations in CodeQL queries](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filePath, int startLine, int startColumn, int endLine, int endColumn
) {
this.asClass()
.getLocation()
.hasLocationInfo(filePath, startLine, startColumn, endLine, endColumn)
or
this.isBuiltin() and
filePath = "" and
startLine = 0 and
startColumn = 0 and
endLine = 0 and
endColumn = 0
}
}
private import ExceptionTypes
predicate incorrectExceptOrder(ExceptStmt ex1, ExceptType cls1, ExceptStmt ex2, ExceptType cls2) {
exists(int i, int j, Try t |

View File

@@ -1,67 +0,0 @@
#!/bin/bash
#
# Build a local Python extractor pack from source.
#
# Usage with the CodeQL CLI (run from the repository root):
#
# codeql database create <db> -l python -s <src> --search-path .
# codeql test run --search-path . python/ql/test/<test-dir>
#
set -eux
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
platform="linux64"
elif [[ "$OSTYPE" == "darwin"* ]]; then
platform="osx64"
else
echo "Unknown OS"
exit 1
fi
cd "$(dirname "$0")/.."
# Build the tsg-python Rust binary
(cd extractor/tsg-python && cargo build --release)
tsg_bin="extractor/tsg-python/target/release/tsg-python"
# Generate python3src.zip from the Python extractor source.
# make_zips.py creates the zip in the source directory and then copies it to the
# given output directory. We use a temporary directory to avoid a same-file copy
# error, then move the zip back.
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
(cd extractor && python3 make_zips.py "$tmpdir")
cp "$tmpdir/python3src.zip" extractor/python3src.zip
# Assemble the extractor pack
rm -rf extractor-pack
mkdir -p extractor-pack/tools/${platform}
# Root-level metadata and schema files
cp codeql-extractor.yml extractor-pack/
cp ql/lib/semmlecode.python.dbscheme extractor-pack/
cp ql/lib/semmlecode.python.dbscheme.stats extractor-pack/
# Python extractor engine files (into tools/)
cp extractor/python_tracer.py extractor-pack/tools/
cp extractor/index.py extractor-pack/tools/
cp extractor/setup.py extractor-pack/tools/
cp extractor/convert_setup.py extractor-pack/tools/
cp extractor/get_venv_lib.py extractor-pack/tools/
cp extractor/imp.py extractor-pack/tools/
cp extractor/LICENSE-PSF.md extractor-pack/tools/
cp extractor/python3src.zip extractor-pack/tools/
cp -r extractor/data extractor-pack/tools/
# Shell tool scripts (autobuild, pre-finalize, lgtm-scripts)
cp tools/autobuild.sh extractor-pack/tools/
cp tools/autobuild.cmd extractor-pack/tools/
cp tools/pre-finalize.sh extractor-pack/tools/
cp tools/pre-finalize.cmd extractor-pack/tools/
cp -r tools/lgtm-scripts extractor-pack/tools/
# Downgrades
cp -r downgrades extractor-pack/
# Platform-specific Rust binary
cp "${tsg_bin}" extractor-pack/tools/${platform}/tsg-python

View File

@@ -45,16 +45,13 @@ extensible predicate additionalExternalFile(string relativePath);
/** A file. */
class File extends Container, Impl::File {
pragma[nomagic]
private predicate isAdditionalExternalFile() { additionalExternalFile(this.getRelativePath()) }
/**
* Holds if this file was extracted from the source code of the target project
* (rather than another location such as inside a dependency).
*/
predicate fromSource() {
exists(ExtractorStep s | s.getAction() = "Extract" and s.getFile() = this) and
not this.isAdditionalExternalFile()
not additionalExternalFile(this.getRelativePath())
}
/**

View File

@@ -63,35 +63,6 @@ signature module InputSig<LocationSig Location> {
DataFlowType getNodeType(Node node);
/**
* Gets a special type to use for parameter node `p` belonging to callables with a
* source node where a source call context `FlowFeature` is used, if any.
*
* This can be used to prevent lambdas from being resolved, when a concrete call
* context is needed. Example:
*
* ```csharp
* void Foo(Action<string> a)
* {
* var x = Source();
* a(x); // (1)
* a = s => Sink(s); // (2)
* a(x); // (3)
* }
*
* void Bar()
* {
* Foo(s => Sink(s)); // (4)
* }
* ```
*
* If a source call context flow feature is used, `a` can be assigned a special
* type that is incompatible with the type of _any_ lambda expression, which will
* prevent the call edge from (1) to (4). Note that the call edge from (3) to (2)
* will still be valid.
*/
default DataFlowType getSourceContextParameterNodeType(Node p) { none() }
predicate nodeIsHidden(Node node);
class DataFlowExpr;

View File

@@ -1103,16 +1103,6 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
private module FwdTypeFlowInput implements TypeFlowInput {
predicate enableTypeFlow = Param::enableTypeFlow/0;
pragma[nomagic]
predicate isParameterNodeInSourceCallContext(ParamNode p) {
hasSourceCallCtx() and
exists(Node source, DataFlowCallable c |
Config::isSource(pragma[only_bind_into](source), _) and
nodeEnclosingCallable(source, c) and
nodeEnclosingCallable(p, c)
)
}
predicate relevantCallEdgeIn = PrevStage::relevantCallEdgeIn/2;
predicate relevantCallEdgeOut = PrevStage::relevantCallEdgeOut/2;
@@ -1420,8 +1410,6 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
private module RevTypeFlowInput implements TypeFlowInput {
predicate enableTypeFlow = Param::enableTypeFlow/0;
predicate isParameterNodeInSourceCallContext(ParamNode p) { none() }
predicate relevantCallEdgeIn(Call call, Callable c) {
flowOutOfCallAp(call, c, _, _, _, _, _)
}

View File

@@ -1893,9 +1893,6 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
signature module TypeFlowInput {
predicate enableTypeFlow();
/** Holds if `p` is a parameter of a callable with a source node that has a call context. */
predicate isParameterNodeInSourceCallContext(ParamNode p);
/** Holds if the edge is possibly needed in the direction `call` to `c`. */
predicate relevantCallEdgeIn(Call call, Callable c);
@@ -1956,9 +1953,6 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
/**
* Holds if a sequence of calls may propagate the value of `arg` to some
* argument-to-parameter call edge that strengthens the static type.
*
* This predicate is a reverse flow computation, starting at calls that
* strengthen the type and then following relevant call edges backwards.
*/
pragma[nomagic]
private predicate trackedArgTypeCand(ArgNode arg) {
@@ -1993,9 +1987,6 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
* Holds if `p` is part of a value-propagating call path where the
* end-points have stronger types than the intermediate parameter and
* argument nodes.
*
* This predicate is a forward flow computation, intersecting with the
* reverse flow computation done in `trackedArgTypeCand`.
*/
private predicate trackedParamType(ParamNode p) {
exists(Call call1, Callable c1, ArgNode argOut, Call call2, Callable c2, ArgNode argIn |
@@ -2022,8 +2013,6 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
typeStrongerThanFilter(at, pt)
)
or
Input::isParameterNodeInSourceCallContext(p)
or
exists(ArgNode arg |
trackedArgType(arg) and
relevantCallEdge(_, _, arg, p) and
@@ -2115,12 +2104,8 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
* context.
*/
private predicate typeFlowParamType(ParamNode p, Type t, boolean cc) {
exists(Callable c | Input::dataFlowNonCallEntry(c, cc) |
cc = true and
nodeEnclosingCallable(p, c) and
t = getSourceContextParameterNodeType(p)
or
(cc = false or not exists(getSourceContextParameterNodeType(p))) and
exists(Callable c |
Input::dataFlowNonCallEntry(c, cc) and
trackedParamWithType(p, t, c)
)
or