Compare commits

..

253 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
5a786ac4e0 Fix captured variable liveness and re-enable toString overrides
- Extend synthetic uncertain reads to function exits of any function
  that writes a captured variable, not just the declaring function.
  This ensures writes to captured variables inside closures remain
  live (matching the old `v.isCaptured()` liveness shortcut).
- Uncomment toString overrides for SsaExplicitDefinition, SsaVariableCapture,
  SsaPhiNode, and SsaVariable to restore original output formats.
- Revert test expected files to pre-test-changes state matching the
  correct toString formats and capture variable results.

Agent-Logs-Url: https://github.com/github/codeql/sessions/6dbf9d42-b2e2-42a2-984b-8ea31df4e633

Co-authored-by: owen-mc <62447351+owen-mc@users.noreply.github.com>
2026-03-25 19:11:42 +00:00
Owen Mansel-Chan
a8997679b5 Test changes to investigate 2026-03-25 16:09:16 +00:00
Owen Mansel-Chan
c0ebdd437a Accept trivially correct test changes 2026-03-25 16:07:54 +00:00
Owen Mansel-Chan
cb590537c6 Fix compiler errors 2026-03-25 15:26:56 +00:00
copilot-swe-agent[bot]
341354f76c Add captured variable liveness fix and fix override annotations
Co-authored-by: owen-mc <62447351+owen-mc@users.noreply.github.com>
Agent-Logs-Url: https://github.com/github/codeql/sessions/b400ebd5-4095-401e-8811-fb550600b3c4
2026-03-25 15:26:54 +00:00
copilot-swe-agent[bot]
87f92f36d0 Fix override annotations and remove final predicate clashes in SSA.qll
Co-authored-by: owen-mc <62447351+owen-mc@users.noreply.github.com>
Agent-Logs-Url: https://github.com/github/codeql/sessions/b400ebd5-4095-401e-8811-fb550600b3c4
2026-03-25 15:26:52 +00:00
copilot-swe-agent[bot]
e467cf6482 Make Go use the shared SSA library (codeql.ssa.Ssa)
Co-authored-by: owen-mc <62447351+owen-mc@users.noreply.github.com>
Agent-Logs-Url: https://github.com/github/codeql/sessions/b400ebd5-4095-401e-8811-fb550600b3c4
2026-03-25 15:26:50 +00:00
Owen Mansel-Chan
e3bcd3bdd5 Improve SSA tests for variables in closures 2026-03-25 15:26:38 +00:00
copilot-swe-agent[bot]
7c38dc34f3 Initial plan 2026-03-24 15:38:32 +00:00
Owen Mansel-Chan
fd8821fcb5 Merge pull request #21475 from owen-mc/rust/mad-barriers
Rust: Add support for defining barriers and barrier guards using models-as-data
2026-03-24 09:31:24 +00:00
Tom Hvitved
cc99867969 Merge pull request #21511 from hvitved/ruby/empty-stats
Ruby: Use empty DB stats
2026-03-24 08:25:43 +01:00
Mathias Vorreiter Pedersen
680ea0b960 Merge pull request #21552 from MathiasVP/more-public-dataflow-apis
C++: Expose indirect instructions and indirect parameters in dataflow
2026-03-23 17:46:14 +00:00
Mario Campos
a5763303fc Merge pull request #21557 from github/rc/3.21
Merge back remaining changes from rc/3.21
2026-03-23 12:28:34 -05:00
Owen Mansel-Chan
8d16a2b4fa Fix parameter -> argument in QLDoc 2026-03-23 16:24:03 +00:00
Owen Mansel-Chan
97ebc0e839 Update QLDoc in FlowBarrier.qll 2026-03-23 16:22:27 +00:00
Owen Mansel-Chan
d82fc67b36 Fix QLDoc formatting 2026-03-23 16:11:22 +00:00
Mathias Vorreiter Pedersen
8cebf510dc C++: Reword the change note from #21458. 2026-03-23 13:45:46 +00:00
Mathias Vorreiter Pedersen
b5723bd75d Merge branch 'main' into more-public-dataflow-apis 2026-03-23 13:43:01 +00:00
Mathias Vorreiter Pedersen
fef314e27f C++: Add change note. 2026-03-23 13:39:15 +00:00
Mathias Vorreiter Pedersen
1363c54a9f C++: Add 'asIndirectInstruction' as a public predicate. 2026-03-23 13:28:33 +00:00
Mathias Vorreiter Pedersen
09caeca7e9 C++: Move parameter indirection nodes into the public API. 2026-03-23 13:27:20 +00:00
Tom Hvitved
0d0d34cc71 Merge pull request #21498 from Gregro/csharp/fix-log-forging-extension-methods
C#: Fix false positives in cs/log-forging for extension methods
2026-03-23 11:24:12 +01:00
Jeroen Ketema
be245357cc Merge pull request #21458 from github/jeongsoolee09/add-getIndirectionIndex
Add `IndirectUninitializedNode` and related helper predicates
2026-03-23 11:03:57 +01:00
Jeroen Ketema
ee00b98476 Update cpp/ql/lib/change-notes/2026-03-20-add-indirect-uninitialized-node.md 2026-03-23 10:44:21 +01:00
Jeongsoo Lee
6ae32f22a8 Merge branch 'main' into jeongsoolee09/add-getIndirectionIndex 2026-03-22 11:51:14 -04:00
Gregro
a59c865328 let interprocedural analysis handle source-available extension methods for LogForgingLogMessageSink's 2026-03-21 20:05:08 +00:00
Gregro
d0c48893f5 update test helper to use more robust .ReplaceLineEndings() sanitizer 2026-03-21 20:05:08 +00:00
Gregro
d99247cf13 Clarify static extension method class name 2026-03-21 20:05:08 +00:00
Gregro
a9eb801fea C#: Fix false positives in cs/log-forging for extension methods 2026-03-21 20:05:08 +00:00
Tom Hvitved
9a4bc69843 Merge pull request #21510 from hvitved/ci/remove-ruby-checks
CI: Remove Ruby checks
2026-03-21 08:04:17 +01:00
Jeongsoo Lee
d4fef1c68e Merge branch 'main' into jeongsoolee09/add-getIndirectionIndex 2026-03-20 10:01:05 -07:00
Jeongsoo Lee
d2fcced5ad Add a feature change note 2026-03-20 09:59:12 -07:00
Owen Mansel-Chan
093c27955f Fix incorrect QLDoc
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-20 15:24:15 +00:00
Tom Hvitved
f99f26f908 Merge pull request #21464 from hvitved/rust/type-inference-trait-bound-impl-overlap
Rust: Disambiguate types inferred from trait bounds
2026-03-20 15:14:24 +01:00
Simon Friis Vindum
f6c81ff30a Merge pull request #21512 from paldepind/cpp/extraction-information
C++: Add `cpp/extraction-information` query
2026-03-20 14:12:59 +01:00
Tom Hvitved
4b364639a2 Ruby: Fix join orders following DB stats removal 2026-03-20 13:13:38 +01:00
Owen Mansel-Chan
bde9378cee Update MaD barrier guard test output 2026-03-20 11:10:08 +00:00
Owen Mansel-Chan
769b3a6aae Instantiate flow barrier guards from MaD 2026-03-20 11:08:53 +00:00
Owen Mansel-Chan
7d65baccb2 Add FlowBarrierGuard to FlowBarrier.qll 2026-03-20 11:08:33 +00:00
Owen Mansel-Chan
77cb35380c Add MaD barrier guard model to make test pass 2026-03-20 11:06:41 +00:00
Owen Mansel-Chan
c5457d3e30 Add (failing) test for MaD barrier guard 2026-03-20 11:06:39 +00:00
Owen Mansel-Chan
2f0d3288ce Misc: fix typos in QLDocs 2026-03-20 11:06:38 +00:00
Owen Mansel-Chan
93c656065d Add test for MaD barriers 2026-03-20 11:06:36 +00:00
Owen Mansel-Chan
e86ce8feed Instantiate flow barriers from MaD 2026-03-20 11:06:35 +00:00
Owen Mansel-Chan
d3177b9e82 Add FlowBarrier.qll 2026-03-20 11:06:33 +00:00
Owen Mansel-Chan
f4550544ce Shared: Add barrierElement in FlowSummaryImpl.qll 2026-03-20 11:06:32 +00:00
Owen Mansel-Chan
f9521e9e88 Update interpretModelForTest 2026-03-20 11:06:30 +00:00
Owen Mansel-Chan
f342bae962 Update empty.model.yml 2026-03-20 11:06:29 +00:00
Owen Mansel-Chan
bceab0b44e Add extensible predicates 2026-03-20 11:06:26 +00:00
Jeroen Ketema
02f8984aff Merge pull request #21522 from jketema/swift-linux-test
Swift: Fix typo
2026-03-20 12:04:27 +01:00
Jeroen Ketema
b63e34d467 Swift: Fix typo 2026-03-20 11:34:19 +01:00
Óscar San José
ec726f5941 Merge pull request #21486 from github/post-release-prep/codeql-cli-2.25.0
Post-release preparation for codeql-cli-2.25.0
2026-03-20 11:23:20 +01:00
Geoffrey White
208ae7aa01 Merge pull request #21514 from geoffw0/suspicioussizeof
C++: Fix an issue with cpp/suspicious-add-sizeof in BMN databases
2026-03-20 09:41:39 +00:00
Geoffrey White
be746b775b Merge pull request #21493 from MarkLee131/fix/format-string-fp-in-printf-impl
C++: exclude printf implementation internals from uncontrolled format string sinks
2026-03-20 09:21:48 +00:00
Simon Friis Vindum
bc518c08c7 C++: Fix grammar in comment
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-20 09:19:59 +01:00
Paolo Tranquilli
06ea72ccc7 Merge pull request #21517 from github/dependabot/bazel/bazel_skylib-1.9.0
Bump bazel_skylib from 1.8.1 to 1.9.0
2026-03-20 09:18:44 +01:00
Paolo Tranquilli
57086f60b9 Merge pull request #21518 from github/dependabot/bazel/rules_java-9.6.1
Bump rules_java from 9.0.3 to 9.6.1
2026-03-20 09:18:16 +01:00
Kaixuan Li
6452cc549f Merge branch 'github:main' into fix/format-string-fp-in-printf-impl 2026-03-20 10:15:56 +08:00
Kaixuan Li
f59bacab30 Merge pull request #1 from geoffw0/changenote
C++: Change note.
2026-03-20 10:14:24 +08:00
Jeongsoo Lee
356905ba36 Merge branch 'jeongsoolee09/add-getIndirectionIndex' of github.com:github/codeql into jeongsoolee09/add-getIndirectionIndex 2026-03-19 15:51:37 -07:00
Jeongsoo Lee
dc291ffad7 Address code review 2026-03-19 15:51:00 -07:00
Jeongsoo Lee
d191d09c55 Apply suggestions from code review
Co-authored-by: Jeroen Ketema <93738568+jketema@users.noreply.github.com>
2026-03-19 15:48:47 -07:00
Geoffrey White
9c6276ef48 C++: Change note. 2026-03-19 16:24:35 +00:00
Tom Hvitved
21f2c81f24 Merge pull request #21509 from hvitved/ci/remove-compile-queries
CI: Remove `compile-queries.yml`
2026-03-19 17:21:09 +01:00
Óscar San José
d30aab47ea Merge branch 'main' into post-release-prep/codeql-cli-2.25.0 2026-03-19 16:57:20 +01:00
dependabot[bot]
fef758998c Bump rules_java from 9.0.3 to 9.6.1
Bumps [rules_java](https://github.com/bazelbuild/rules_java) from 9.0.3 to 9.6.1.
- [Release notes](https://github.com/bazelbuild/rules_java/releases)
- [Commits](https://github.com/bazelbuild/rules_java/compare/9.0.3...9.6.1)

---
updated-dependencies:
- dependency-name: rules_java
  dependency-version: 9.6.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-19 15:48:18 +00:00
dependabot[bot]
b9592fef2d Bump bazel_skylib from 1.8.1 to 1.9.0
Bumps [bazel_skylib](https://github.com/bazelbuild/bazel-skylib) from 1.8.1 to 1.9.0.
- [Release notes](https://github.com/bazelbuild/bazel-skylib/releases)
- [Changelog](https://github.com/bazelbuild/bazel-skylib/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bazelbuild/bazel-skylib/compare/1.8.1...1.9.0)

---
updated-dependencies:
- dependency-name: bazel_skylib
  dependency-version: 1.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-19 15:48:11 +00:00
Paolo Tranquilli
7a33e2f539 Merge pull request #21508 from github/dependabot/bazel/rules_pkg-1.2.0
Bump rules_pkg from 1.0.1 to 1.2.0
2026-03-19 16:27:45 +01:00
Paolo Tranquilli
34101b5ca0 Merge pull request #21448 from github/redsun82/update-rules-rust
Update `rules_rust` 0.68.1.codeql.1 → 0.69.0, drop local patch
2026-03-19 16:16:34 +01:00
Geoffrey White
07db9cf3c4 Merge pull request #21421 from geoffw0/wrongtypeformat
C++: Add some test cases for cpp/wrong-type-format-argument
2026-03-19 14:25:33 +00:00
Geoffrey White
92c9a8e146 Update cpp/ql/test/query-tests/Security/CWE/CWE-468/semmle/SuspiciousAddWithSizeof/buildless.cpp 2026-03-19 13:51:03 +00:00
Geoffrey White
21cb11ea5d C++: Change note. 2026-03-19 13:29:41 +00:00
Simon Friis Vindum
4c525ce7ab C++: Add cpp/extraction-information query 2026-03-19 14:29:31 +01:00
Geoffrey White
0f794b57ed C++: Fix the issue. 2026-03-19 13:16:16 +00:00
Geoffrey White
2e987f8d78 C++: Add test cases emulating cpp/suspicious-add-sizeof in buildless mode. 2026-03-19 13:00:58 +00:00
Óscar San José
2139b97628 Merge branch 'main' into post-release-prep/codeql-cli-2.25.0 2026-03-19 13:07:00 +01:00
Tom Hvitved
7fc1d53ede Rust: Disambiguate types inferred from trait bounds 2026-03-19 12:57:22 +01:00
Paolo Tranquilli
50d83ada95 Merge branch 'main' into redsun82/update-rules-rust 2026-03-19 12:50:00 +01:00
dependabot[bot]
10678d3a42 Bump rules_pkg from 1.0.1 to 1.2.0
Bumps [rules_pkg](https://github.com/bazelbuild/rules_pkg) from 1.0.1 to 1.2.0.
- [Release notes](https://github.com/bazelbuild/rules_pkg/releases)
- [Changelog](https://github.com/bazelbuild/rules_pkg/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bazelbuild/rules_pkg/compare/1.0.1...1.2.0)

---
updated-dependencies:
- dependency-name: rules_pkg
  dependency-version: 1.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-19 11:31:03 +00:00
Paolo Tranquilli
7d538988a6 Merge pull request #21507 from github/dependabot/bazel/rules_go-0.60.0
Bump rules_go from 0.59.0 to 0.60.0
2026-03-19 12:29:17 +01:00
dependabot[bot]
7f17b7716d Bump rules_go from 0.59.0 to 0.60.0
Bumps [rules_go](https://github.com/bazel-contrib/rules_go) from 0.59.0 to 0.60.0.
- [Release notes](https://github.com/bazel-contrib/rules_go/releases)
- [Commits](https://github.com/bazel-contrib/rules_go/compare/v0.59.0...v0.60.0)

---
updated-dependencies:
- dependency-name: rules_go
  dependency-version: 0.60.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-19 10:43:19 +00:00
Paolo Tranquilli
b57fa1bffa Merge pull request #21505 from github/dependabot/bazel/rules_cc-0.2.17
Bump rules_cc from 0.2.16 to 0.2.17
2026-03-19 11:41:28 +01:00
dependabot[bot]
662b1e7df6 Bump rules_cc from 0.2.16 to 0.2.17
Bumps [rules_cc](https://github.com/bazelbuild/rules_cc) from 0.2.16 to 0.2.17.
- [Release notes](https://github.com/bazelbuild/rules_cc/releases)
- [Commits](https://github.com/bazelbuild/rules_cc/compare/0.2.16...0.2.17)

---
updated-dependencies:
- dependency-name: rules_cc
  dependency-version: 0.2.17
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-19 10:02:30 +00:00
Tom Hvitved
750f1ae8e9 Ruby: Use empty DB stats 2026-03-19 10:18:42 +01:00
Tom Hvitved
e0b06c8e72 CI: Remove Ruby checks
Have been superseded by an internal checks.
2026-03-19 10:15:36 +01:00
Tom Hvitved
8d6aceb008 CI: Remove compile-queries.yml
Has been superseded by an internal check.
2026-03-19 10:10:38 +01:00
Tom Hvitved
3fad6bdc0c Merge pull request #21495 from hvitved/rust/fix-bad-joins
Rust: Fix two bad joins
2026-03-19 09:53:22 +01:00
Paolo Tranquilli
518d170acd Merge pull request #21499 from github/redsun82/dependabot-exclude-bazel-registry
Dependabot: ignore modules in our bazel registry
2026-03-19 09:25:28 +01:00
Paolo Tranquilli
b9ad36c11d Depdendabot: ignore modules in the our bazel registry
These come from the upstream registry and should just be left alone.
2026-03-19 09:15:25 +01:00
Paolo Tranquilli
4ca071210b Merge branch 'main' into redsun82/update-rules-rust 2026-03-19 08:36:31 +01:00
Kaixuan Li
1ddf81c58c Merge branch 'main' into fix/format-string-fp-in-printf-impl 2026-03-19 14:36:50 +08:00
Kaixuan Li
c155394f25 the [] syntax directly
Co-authored-by: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
2026-03-19 14:36:28 +08:00
Kaixuan Li
2c76e6e637 use American spellings in documentation
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-03-19 14:35:45 +08:00
Tom Hvitved
2ff5c2c234 Rust: Fix two bad joins
Before
```
Evaluated relational algebra for predicate TypeInference::DeconstructionPatMatchingInput::Access.getNodeAt/1#dispred#cc149bc2@88f6f09n with tuple counts:
           142521   ~1%    {3} r1 = JOIN num#FunctionType::TReturnFunctionPosition#a15fd6be WITH TypeInference::DeconstructionPatMatchingInput::Access#a2676dcb CARTESIAN PRODUCT OUTPUT Rhs.0, Lhs.0, Rhs.0

           131938   ~0%    {3} r2 = JOIN `TupleStructPat::Generated::TupleStructPat.getField/1#dispred#ac9c1af6` WITH TypeInference::DeconstructionPatMatchingInput::Access#a2676dcb ON FIRST 1 OUTPUT Lhs.1, Lhs.0, Lhs.2
           131938   ~6%    {3}    | JOIN WITH `FunctionType::FunctionPosition.asPosition/0#dispred#efcc0611_10#join_rhs` ON FIRST 1 OUTPUT Lhs.1, Rhs.1, Lhs.2

          3071346   ~0%    {2} r3 = SCAN `Name::Generated::Name.getText/0#dispred#107a5a39` OUTPUT In.1, In.0
        103064442   ~2%    {3}    | JOIN WITH `StructPat::StructPat.getPatField/1#5e21ea0e_102#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Rhs.2
        103064442   ~3%    {3}    | JOIN WITH TypeInference::DeconstructionPatMatchingInput::Access#a2676dcb ON FIRST 1 OUTPUT Lhs.2, Lhs.1, Lhs.0
        103064438   ~1%    {3}    | JOIN WITH `StructPatField::Generated::StructPatField.getPat/0#dispred#1aadfeff` ON FIRST 1 OUTPUT Lhs.1, Lhs.2, Rhs.1
         20514858   ~2%    {3}    | JOIN WITH `StructField::Generated::StructField.getName/0#dispred#e0248569_10#join_rhs` ON FIRST 1 OUTPUT Lhs.1, Rhs.1, Lhs.2
            59554   ~1%    {3}    | JOIN WITH `StructPat::StructPat.getNthStructField/1#dispred#de537654_021#join_rhs` ON FIRST 2 OUTPUT Rhs.2, Lhs.0, Lhs.2
            59542   ~0%    {3}    | JOIN WITH `FunctionType::FunctionPosition.asPosition/0#dispred#efcc0611_10#join_rhs` ON FIRST 1 OUTPUT Lhs.1, Rhs.1, Lhs.2

           334001   ~0%    {3} r4 = r1 UNION r2 UNION r3
                           return r4

Evaluated relational algebra for predicate TypeInference::ConstructionMatchingInput::Access.getNodeAt/1#dispred#acd835e6@bfb1f1e1 with tuple counts:
          1395153   ~3%    {3} r1 = JOIN TypeInference::ConstructionMatchingInput::PathExprAccess#b7a80c43 WITH num#FunctionType::TReturnFunctionPosition#a15fd6be CARTESIAN PRODUCT OUTPUT Lhs.0, Rhs.0, Lhs.0

            34290   ~3%    {3} r2 = JOIN StructExpr::Generated::StructExpr#d0a89c56 WITH num#FunctionType::TReturnFunctionPosition#a15fd6be CARTESIAN PRODUCT OUTPUT Lhs.0, Rhs.0, Lhs.0

          3071346   ~0%    {2} r3 = SCAN `Name::Generated::Name.getText/0#dispred#107a5a39` OUTPUT In.1, In.0
        145365745   ~0%    {3}    | JOIN WITH `StructExpr::StructExpr.getFieldExpr/1#cd55566d_102#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Rhs.2
        145365745   ~1%    {3}    | JOIN WITH StructExpr::Generated::StructExpr#d0a89c56 ON FIRST 1 OUTPUT Lhs.1, Lhs.0, Lhs.2
         33371514   ~0%    {3}    | JOIN WITH `StructField::Generated::StructField.getName/0#dispred#e0248569_10#join_rhs` ON FIRST 1 OUTPUT Lhs.1, Rhs.1, Lhs.2
           108831   ~0%    {3}    | JOIN WITH `StructExpr::StructExpr.getNthStructField/1#dispred#89ad7e20_021#join_rhs` ON FIRST 2 OUTPUT Rhs.2, Lhs.0, Lhs.2
           108751   ~0%    {3}    | JOIN WITH `FunctionType::FunctionPosition.asPosition/0#dispred#efcc0611_10#join_rhs` ON FIRST 1 OUTPUT Lhs.2, Lhs.1, Rhs.1
           108751   ~4%    {3}    | JOIN WITH `StructExprField::Generated::StructExprField.getExpr/0#dispred#956e6ba1` ON FIRST 1 OUTPUT Lhs.1, Lhs.2, Rhs.1

          1748398   ~4%    {3} r4 = `TypeInference::ConstructionMatchingInput::NonAssocCallAccess.getNodeAt/1#dispred#ef232b1f` UNION r1 UNION r2 UNION r3
                           return r4
```

After
```
Evaluated relational algebra for predicate TypeInference::DeconstructionPatMatchingInput::Access.getNodeAt/1#dispred#cc149bc2@2ea6ebjs with tuple counts:
        142521   ~1%    {3} r1 = JOIN num#FunctionType::TReturnFunctionPosition#a15fd6be WITH TypeInference::DeconstructionPatMatchingInput::Access#a2676dcb CARTESIAN PRODUCT OUTPUT Rhs.0, Lhs.0, Rhs.0

        131938   ~0%    {3} r2 = JOIN `TupleStructPat::Generated::TupleStructPat.getField/1#dispred#ac9c1af6` WITH TypeInference::DeconstructionPatMatchingInput::Access#a2676dcb ON FIRST 1 OUTPUT Lhs.1, Lhs.0, Lhs.2
        131938   ~6%    {3}    | JOIN WITH `FunctionType::FunctionPosition.asPosition/0#dispred#efcc0611_10#join_rhs` ON FIRST 1 OUTPUT Lhs.1, Rhs.1, Lhs.2

        166829   ~3%    {3} r3 = JOIN `_Name::Generated::Name.getText/0#dispred#107a5a39_StructField::Generated::StructField.getName/0#disp__#shared` WITH `StructPat::StructPat.getNthStructField/1#dispred#de537654_201#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Rhs.2
        166817   ~2%    {3}    | JOIN WITH TypeInference::DeconstructionPatMatchingInput::Access#a2676dcb ON FIRST 1 OUTPUT Lhs.2, Lhs.1, Lhs.0
        166817   ~0%    {3}    | JOIN WITH `FunctionType::FunctionPosition.asPosition/0#dispred#efcc0611_10#join_rhs` ON FIRST 1 OUTPUT Lhs.2, Lhs.1, Rhs.1
         59542   ~0%    {3}    | JOIN WITH `StructPat::StructPat.getPatField/1#5e21ea0e` ON FIRST 2 OUTPUT Rhs.2, Lhs.0, Lhs.2
         59542   ~0%    {3}    | JOIN WITH `StructPatField::Generated::StructPatField.getPat/0#dispred#1aadfeff` ON FIRST 1 OUTPUT Lhs.1, Lhs.2, Rhs.1

        334001   ~0%    {3} r4 = r1 UNION r2 UNION r3
                        return r4

Evaluated relational algebra for predicate TypeInference::ConstructionMatchingInput::Access.getNodeAt/1#dispred#acd835e6@c7f267fp with tuple counts:
        1395153   ~3%    {3} r1 = JOIN TypeInference::ConstructionMatchingInput::PathExprAccess#b7a80c43 WITH num#FunctionType::TReturnFunctionPosition#a15fd6be CARTESIAN PRODUCT OUTPUT Lhs.0, Rhs.0, Lhs.0

          34290   ~3%    {3} r2 = JOIN StructExpr::Generated::StructExpr#d0a89c56 WITH num#FunctionType::TReturnFunctionPosition#a15fd6be CARTESIAN PRODUCT OUTPUT Lhs.0, Rhs.0, Lhs.0

         159331   ~0%    {3} r3 = JOIN `_Name::Generated::Name.getText/0#dispred#107a5a39_StructField::Generated::StructField.getName/0#disp__#shared` WITH `StructExpr::StructExpr.getNthStructField/1#dispred#89ad7e20_201#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Rhs.2
         159231   ~3%    {3}    | JOIN WITH StructExpr::Generated::StructExpr#d0a89c56 ON FIRST 1 OUTPUT Lhs.2, Lhs.1, Lhs.0
         159231   ~3%    {3}    | JOIN WITH `FunctionType::FunctionPosition.asPosition/0#dispred#efcc0611_10#join_rhs` ON FIRST 1 OUTPUT Lhs.2, Lhs.1, Rhs.1
         108731   ~0%    {3}    | JOIN WITH `StructExpr::StructExpr.getFieldExpr/1#cd55566d` ON FIRST 2 OUTPUT Rhs.2, Lhs.0, Lhs.2
         108731   ~4%    {3}    | JOIN WITH `StructExprField::Generated::StructExprField.getExpr/0#dispred#956e6ba1` ON FIRST 1 OUTPUT Lhs.1, Lhs.2, Rhs.1

        1748378   ~4%    {3} r4 = `TypeInference::ConstructionMatchingInput::NonAssocCallAccess.getNodeAt/1#dispred#ef232b1f` UNION r1 UNION r2 UNION r3
                         return r4
```
2026-03-18 20:42:57 +01:00
Tom Hvitved
98d8cd1d6d Rust: Make getATypeParameterConstraint return a TypeMention again 2026-03-18 19:52:45 +01:00
Tom Hvitved
e8e46accc0 Rust: Refine AssociatedTypeTypeParameter.toString 2026-03-18 19:52:43 +01:00
Tom Hvitved
06f0c1189f Shared: Generalize SatisfiesConstraint module 2026-03-18 19:52:41 +01:00
Tom Hvitved
082dc61620 Rust: Add type inference test 2026-03-18 19:52:39 +01:00
Tom Hvitved
2e7da72277 Merge pull request #21488 from paldepind/rust/tuple-constructor-self
Rust: Unify handling of struct and tuple constructors
2026-03-18 19:52:06 +01:00
Owen Mansel-Chan
5b17d8cf76 Merge pull request #21472 from owen-mc/adjust-severity/xss-log-injection
Adjust `@security-severity` metadata for XSS and log injection queries
2026-03-18 16:51:14 +00:00
Simon Friis Vindum
f2a0724620 Rust: Use getReturnType 2026-03-18 15:06:34 +01:00
Simon Friis Vindum
b8222167d2 Rust: Ensure that TPositionalArgumentPosition is large enough for struct expressions 2026-03-18 15:06:32 +01:00
Simon Friis Vindum
6efd844180 Rust: Rename into "construction" and "deconstruction" 2026-03-18 15:06:23 +01:00
Geoffrey White
34f405f465 C++: Update test annotations. 2026-03-18 13:13:22 +00:00
Anders Schack-Mulligen
d4a0846c6c Merge pull request #21490 from aschackmull/csharp/enclosing-obinit
C#: Add ObjectInitMethod as enclosing callable for the instance initializers.
2026-03-18 10:03:18 +01:00
Simon Friis Vindum
d180900ab4 Rust: Minor improvements to documentation comments
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-03-17 19:01:22 +01:00
Simon Friis Vindum
97670b3674 Rust: Unify handling of struct and tuple constructors 2026-03-17 16:41:18 +01:00
MarkLee131
ff48ac5434 C++: exclude printf implementation internals from format string sinks 2026-03-17 22:45:38 +08:00
Anders Schack-Mulligen
af63e63686 C#: Accept test changes. 2026-03-17 14:12:18 +01:00
Anders Schack-Mulligen
19faf8f30b C#: Add ObjectInitMethod as enclosing callable for the instance initializers. 2026-03-17 13:38:41 +01:00
Owen Mansel-Chan
3aaee9d981 Change @security-severity for rust/log-injection from 2.6 to 6.1 2026-03-17 12:01:05 +00:00
Tom Hvitved
8e19b05a25 Merge pull request #21355 from hvitved/rust/type-inference-unify
Rust: Unify call resolution logic
2026-03-17 10:38:05 +01:00
Tom Hvitved
1ac9e5a2a4 Rust: Elaborate QL doc on FunctionPosition class 2026-03-17 09:51:15 +01:00
Jeongsoo Lee
3f9ad14473 Factor out common code into an abstract private class 2026-03-16 14:11:34 -07:00
Geoffrey White
9cb1c89a02 C++: Change note. 2026-03-16 19:11:27 +00:00
Geoffrey White
a57f803b37 C++: Address false positive results. 2026-03-16 19:03:10 +00:00
Geoffrey White
1130870168 Merge remote-tracking branch 'upstream/main' into wrongtypeformat 2026-03-16 19:03:02 +00:00
Geoffrey White
8ddfee9971 Merge pull request #21457 from geoffw0/intmultlong
C++: Fix BMN issue with cpp/integer-multiplication-cast-to-long.
2026-03-16 19:02:16 +00:00
Geoffrey White
8df4dfb585 C++: Autoformat. 2026-03-16 16:40:27 +00:00
Geoffrey White
2f7526d70b C++: Clarify doc comment and make build-mode: nonereferences more consistent. 2026-03-16 16:38:59 +00:00
Geoffrey White
eeb09ae389 C++: Fix typo. 2026-03-16 16:12:30 +00:00
Geoffrey White
3c4a386f3f C++: Clarify two cases in the test. 2026-03-16 16:08:35 +00:00
github-actions[bot]
e3dbf5b022 Post-release preparation for codeql-cli-2.25.0 2026-03-16 16:03:22 +00:00
Óscar San José
72534e882b Merge pull request #21483 from github/release-prep/2.25.0
Release preparation for version 2.25.0
2026-03-16 15:45:36 +01:00
Taus
a99b3f2c3b Merge pull request #21459 from github/tausbn/python-fix-missing-relative-imports
Python: Fix resolution of relative imports from namespace packages
2026-03-16 14:59:44 +01:00
Taus
92718a98d0 Python: Add test for package inside namespace package 2026-03-16 12:41:09 +00:00
Taus
e70727524a Python: Rename prints tag to flow
The former was a remnant of copying the setup over from
`ql/test/experimental/import-resolution/importflow.ql`.
2026-03-16 12:37:00 +00:00
github-actions[bot]
d6055754b6 Release preparation for version 2.25.0 2026-03-16 12:15:34 +00:00
Anders Schack-Mulligen
c24b43d01e Merge pull request #21482 from aschackmull/csharp/rangeanalysis-no-split
C#: Remove splitting-awareness from Range Analysis.
2026-03-16 10:54:49 +01:00
Jeroen Ketema
179a4cd41a Merge pull request #21474 from jketema/jketema/swift-linux-2
Swift: Ignore some DB-CHECK results on Linux
2026-03-16 10:50:05 +01:00
Anders Schack-Mulligen
a929c0bf24 C#: Remove splitting-awareness from Range Analysis. 2026-03-16 09:58:14 +01:00
Anders Schack-Mulligen
427ccee3b9 Merge pull request #21473 from aschackmull/csharp/dataflow-no-split
C#: Remove splitting-awareness from data flow.
2026-03-16 09:33:31 +01:00
Asger F
22f16dda85 Merge pull request #21368 from asgerf/browser-sources
JS: Add 'browser' source kinds
2026-03-16 09:24:54 +01:00
Anders Schack-Mulligen
db0a3e38e2 C#: Accept a few irrelevant taint steps. 2026-03-16 09:09:54 +01:00
Anders Schack-Mulligen
e7edf15031 C#: Clean up. 2026-03-16 08:51:51 +01:00
Anders Schack-Mulligen
4c77e0f315 C#: Remove splitting-awareness for local expression steps. 2026-03-16 08:51:51 +01:00
Anders Schack-Mulligen
7124cd4e6e C#: Remove splitting-awareness for source-to-def steps. 2026-03-16 08:51:50 +01:00
Anders Schack-Mulligen
c076992b83 C#: Remove splitting-awareness in ObjectInitializerNode. 2026-03-16 08:51:49 +01:00
Anders Schack-Mulligen
659d8e7c90 C#: Remove splitting-awareness in argumentOf. 2026-03-16 08:51:49 +01:00
Anders Schack-Mulligen
1e8de0511b C#: Remove splitting-awareness in lambda flow. 2026-03-16 08:51:48 +01:00
Anders Schack-Mulligen
bce0a4d2a7 C#: Remove splitting-awareness for store steps. 2026-03-16 08:51:48 +01:00
Anders Schack-Mulligen
2160910d56 C#: Remove splitting-awareness for read steps. 2026-03-16 08:51:47 +01:00
Anders Schack-Mulligen
a5c8a5b5f8 C#: Remove splitting-awareness for taint steps. 2026-03-16 08:51:47 +01:00
Jeongsoo Lee
f8a3ce7bf8 Merge branch 'main' into jeongsoolee09/add-getIndirectionIndex 2026-03-14 16:17:36 -07:00
Jeroen Ketema
f9f1d9eecc Swift: Ignore some DB-CHECK results on Linux 2026-03-13 20:06:57 +01:00
Jeongsoo Lee
d3066af2e2 Create IndirectUninitializedNode and add a bridge from it to LocalVariable
This way the changes do not alter the meaning of `UninitializedNode`.
In the meantime, the code still provides a specialized `Node` type
`IndirectUninitializedNode` to access the nodes behind levels of
indirection.
2026-03-13 11:39:57 -07:00
Jeongsoo Lee
8c03136c25 Revert "Expose the indirection index"
This reverts commit 6c792e69b3.
2026-03-13 10:11:40 -07:00
Owen Mansel-Chan
d52e9bc18c Merge pull request #21370 from github/owen-mc/go/overlay-annotations
Go: Add overlay annotations from script
2026-03-13 16:46:01 +00:00
Owen Mansel-Chan
b8b841cfba Add overlay[loca] in 4 more tests 2026-03-13 16:19:00 +00:00
Owen Mansel-Chan
df9f8ee386 Merge branch 'main' into owen-mc/go/overlay-annotations 2026-03-13 15:55:17 +00:00
Owen Mansel-Chan
99f4930e24 Explicitly mark DataFlowNodes.qll as overlay[local] 2026-03-13 15:23:39 +00:00
Owen Mansel-Chan
e9df9147ad Add overlay annotations in 4 PrintAst tests 2026-03-13 15:03:05 +00:00
Owen Mansel-Chan
f32f85399a Mark various files as overlay[local] 2026-03-13 15:03:02 +00:00
Jonas Jensen
c56feb7644 Go: annotate the standard library with for overlay
This commit is auto-generated with:

    python3 config/add-overlay-annotations.py go
2026-03-13 15:03:01 +00:00
Jonas Jensen
7ef60a8649 Update the overlay annotation script for go
The Go libraries follow their own naming convention for "query
libraries". These need to be exempted from automatic `overlay[local?]`
annotations since otherwise it appears that too many predicates are
evaluated, possibly because of inadequate use of sentinels.
2026-03-13 15:02:58 +00:00
Tom Hvitved
ca2838b361 Address review comments 2026-03-13 15:38:07 +01:00
Tom Hvitved
7a6ab70091 Rust: Add test for free function with context-based typing 2026-03-13 15:36:06 +01:00
Asger F
7d6e08ecf1 Merge pull request #21461 from github/asger/js-shebang-bun-tsx
JS: Recognise bun and tsx in shebang lines
2026-03-13 15:07:12 +01:00
Asger F
dfa6d20072 JS: Replace broken link with plain text 2026-03-13 15:05:07 +01:00
Asger F
821cc0e875 JS: Address PR review comments
- Fix misplaced semicolons in test files (was inside comment, moved before it)
- Update QLdoc comments to reference new browser source kind names
- Update docs to list browser source kinds and fix outdated 'only remote' note

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-13 14:58:04 +01:00
Tom Hvitved
7094fb07a4 Rust: Replace FunctionPosition with FunctionPositionAdj 2026-03-13 14:37:40 +01:00
Owen Mansel-Chan
52809133f5 Add change notes 2026-03-13 11:10:43 +00:00
Owen Mansel-Chan
056aa342fe Change @security-severity for log injection queries from 7.8 to 6.1 2026-03-13 10:02:01 +00:00
Owen Mansel-Chan
f58a6e5d3a Change @security-severity for XSS queries from 6.1 to 7.8 2026-03-13 10:01:02 +00:00
Anders Schack-Mulligen
f11815c633 Merge pull request #21471 from aschackmull/csharp/rm-prebasicblock
C#: Delete PreBasicBlocks.
2026-03-13 08:54:06 +01:00
Owen Mansel-Chan
52cfd49087 Merge pull request #21469 from github/dependabot/go_modules/go/extractor/extractor-dependencies-7af763c229
Bump the extractor-dependencies group across 1 directory with 2 updates
2026-03-13 07:06:44 +00:00
Anders Schack-Mulligen
8c1c039edf C#: Delete PreBasicBlocks. 2026-03-13 08:00:08 +01:00
dependabot[bot]
c9e0927992 Bump the extractor-dependencies group across 1 directory with 2 updates
Bumps the extractor-dependencies group with 2 updates in the /go/extractor directory: [golang.org/x/mod](https://github.com/golang/mod) and [golang.org/x/tools](https://github.com/golang/tools).


Updates `golang.org/x/mod` from 0.33.0 to 0.34.0
- [Commits](https://github.com/golang/mod/compare/v0.33.0...v0.34.0)

Updates `golang.org/x/tools` from 0.42.0 to 0.43.0
- [Release notes](https://github.com/golang/tools/releases)
- [Commits](https://github.com/golang/tools/compare/v0.42.0...v0.43.0)

---
updated-dependencies:
- dependency-name: golang.org/x/mod
  dependency-version: 0.34.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: extractor-dependencies
- dependency-name: golang.org/x/tools
  dependency-version: 0.43.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: extractor-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-13 03:04:44 +00:00
Jeroen Ketema
d5f667e585 Merge pull request #21467 from jketema/jketema/swift-linux
Swift: Disable stack protector pass
2026-03-12 22:42:51 +01:00
Jeroen Ketema
b758732a28 Merge pull request #21468 from jketema/jketema/swift-lines
Swift: Limit successfully extracted lines
2026-03-12 17:24:28 +01:00
Jeroen Ketema
ba3fadbf20 Swift: Rename function 2026-03-12 16:37:13 +01:00
Owen Mansel-Chan
d7d1554461 Merge pull request #21465 from owen-mc/go/small-tweaks
Go: improve detection of type expressions when database is missing some type information
2026-03-12 14:58:16 +00:00
Jeroen Ketema
12e0f3f359 Swift: Limit successfully extracted lines 2026-03-12 15:46:23 +01:00
Owen Mansel-Chan
0bb6ff58cc Merge pull request #21466 from owen-mc/go/add-nil-helper-predicate
Go: Add and use `exprRefersToNil` predicate
2026-03-12 14:36:03 +00:00
Jeroen Ketema
b9c0aca11a Swift: Fix formatting 2026-03-12 15:00:18 +01:00
Jeroen Ketema
ee3674cb80 Swift: Disable stack protector pass 2026-03-12 14:43:05 +01:00
Taus
3ee369b710 Python: Add change note 2026-03-12 13:29:24 +00:00
Taus
e16bb226c0 Python: Fix resolution of relative imports from namespace packages
The fix may look a bit obscure, so here's what's going on.

When we see `from . import helper`, we create an `ImportExpr` with level
equal to 1 (corresponding to the number of dots). To resolve such
imports, we compute the name of the enclosing package, as part of
`ImportExpr.qualifiedTopName()`. For this form of import expression, it
is equivalent to `this.getEnclosingModule().getPackageName()`. But
`qualifiedTopName` requires that `valid_module_name` holds for its
result, and this was _not_ the case for namespace packages.

To fix this, we extend `valid_module_name` to include the module names
of _any_ folder, not just regular package (which are the ones where
there's a `__init__.py` in the folder). Note that this doesn't simply
include all folders -- only the ones that result in valid module names
in Python.
2026-03-12 13:29:23 +00:00
Taus
48bf4fd82a Python: Add test for missing relative import in namespace packages 2026-03-12 13:29:19 +00:00
Owen Mansel-Chan
c271755985 Add and use exprRefersToNil predicate 2026-03-12 13:28:57 +00:00
Owen Mansel-Chan
a16c43881b Use "database" instead of "snapshot" in QLDocs 2026-03-12 13:28:06 +00:00
Owen Mansel-Chan
39e0382089 Improve QLDoc for isTypeExprTopDown 2026-03-12 13:28:05 +00:00
Owen Mansel-Chan
22e012c6f4 Expand isTypeExprTopDown
We should be using all subtypes of `FieldBase`. This allows us to find
more type expressions, and is also simpler to evaluate.
2026-03-12 13:28:03 +00:00
Tom Hvitved
1b6f3a43ef Rust: Unify type inference logic for associated functions 2026-03-12 10:31:35 +01:00
Asger F
b8c44be599 Add QL test for bun/tsx shebang recognition in TypeScript files
Add test files with #!/usr/bin/env bun, #!/usr/bin/env tsx, and
#!/usr/bin/env node shebangs. The query lists extracted .ts files,
verifying that all three shebangs are recognized and the files are
not skipped by the extractor.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-12 10:09:56 +01:00
Asger F
84d1828a9c JavaScript extractor: recognise bun and tsx in shebang lines
Update the shebang regexp (renamed NODE_INVOCATION -> JS_INVOCATION) to
also match 'bun' and 'tsx' so that scripts using these runtimes are
correctly identified as JavaScript files.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-12 09:35:36 +01:00
Tom Hvitved
ca7017f3d7 Rust: Add more type inference tests 2026-03-12 09:02:40 +01:00
Mario Campos
f2e7dca65c Merge pull request #21454 from github/mario-campos-patch-1
Correct comment about AES crypto algorithm strength
2026-03-11 22:43:21 -05:00
Mario Campos
b9b3b3a0b5 Empty commit for missed Green Check 2026-03-11 22:37:20 -05:00
Jeongsoo Lee
6c792e69b3 Expose the indirection index 2026-03-11 13:53:24 -07:00
Geoffrey White
4a39055322 C++: Change note. 2026-03-11 17:52:34 +00:00
Geoffrey White
6552c849f0 C++: Fix BMN issue in cpp/integer-multiplication-cast-to-long. 2026-03-11 17:49:36 +00:00
Geoffrey White
00d8a10051 C++: Add Function.hasAmbiguousReturnType. 2026-03-11 17:47:32 +00:00
Geoffrey White
da7da80b2b C++: Add pseudo-buildless test cases (some missing declarations). 2026-03-11 17:47:25 +00:00
Asger F
5db30c9947 JS: Add change note 2026-03-11 15:40:07 +01:00
Mario Campos
6fb10555ff Correct comment about AES crypto algorithm strength 2026-03-11 09:27:03 -05:00
Ian Lynagh
bbd02b855b Merge pull request #21424 from github/idrissrio/cpp/overlay/discard
C/C++ overlay: update discard mechanism
2026-03-11 13:45:52 +00:00
Idriss Riouak
48a03e2a04 Apply suggestions from code review
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-11 14:25:53 +01:00
idrissrio
a92d97744f C/C++ overlay: address review comment 2026-03-11 14:25:52 +01:00
idrissrio
ef6c1a9968 C/C++ overlay: fix failing header_dependency test 2026-03-11 14:25:50 +01:00
idrissrio
72142b51f7 C/C++ overlay: switch to updated discard strategy 2026-03-11 14:25:49 +01:00
Tom Hvitved
c06d4d2647 Merge pull request #21422 from hvitved/rust/type-mention-refactor
Rust: Small refactor in `TypeMention.qll`
2026-03-11 14:01:03 +01:00
Asger F
4a001f960f JS: Add tests in request forgery queries 2026-03-11 13:53:25 +01:00
Asger F
1253553aec JS: Add browser source kinds 2026-03-11 13:50:07 +01:00
Ian Lynagh
68dfa5c83b Merge pull request #21451 from igfoo/igfoo/fix-build
Revert "Bump rules_android from 0.6.4 to 0.7.1"
2026-03-11 12:27:20 +00:00
Ian Lynagh
25a20f74f0 Revert "Bump rules_android from 0.6.4 to 0.7.1"
This reverts commit c7349740f0.

It was making the build fail
2026-03-11 11:54:18 +00:00
Taus
5a65282241 Merge pull request #21429 from github/tausbn/fix-bad-join-in-method-call-order
Python: Fix bad join in method call order computation
2026-03-10 18:17:35 +01:00
Paolo Tranquilli
2e04d4b888 Merge branch 'main' into redsun82/update-rules-rust 2026-03-10 16:43:50 +01:00
Paolo Tranquilli
79499c240a Merge pull request #21444 from github/dependabot/bazel/googletest-1.17.0.bcr.2
Bump googletest from 1.14.0.bcr.1 to 1.17.0.bcr.2
2026-03-10 16:41:38 +01:00
Paolo Tranquilli
267a46d01b Merge pull request #21445 from github/dependabot/bazel/rules_shell-0.6.1
Bump rules_shell from 0.5.0 to 0.6.1
2026-03-10 16:41:24 +01:00
Ian Lynagh
341059d2d0 Merge pull request #21437 from igfoo/igfoo/onemk
C++: Small simplification
2026-03-10 15:36:38 +00:00
Paolo Tranquilli
79841bbc00 Update rules_rust 0.68.1.codeql.1 → 0.69.0, drop local patch
The `include_rmeta_in_stdlib.patch` is included upstream in 0.69.0.
Remove the local registry entry and regenerate vendored deps.
2026-03-10 16:20:50 +01:00
Paolo Tranquilli
3c3c58b0a9 Merge pull request #21443 from github/dependabot/bazel/rules_android-0.7.1
Bump rules_android from 0.6.4 to 0.7.1
2026-03-10 16:06:40 +01:00
Paolo Tranquilli
9bf1072a01 Merge pull request #21447 from github/revert-21414-redsun82/rerun-slash-command
Revert "Add `/rerun` slash command for failed internal checks"
2026-03-10 15:55:45 +01:00
Paolo Tranquilli
a5f23ade8c Revert "Add /rerun slash command for failed internal checks" 2026-03-10 14:43:59 +01:00
Paolo Tranquilli
017b6f2e44 Merge pull request #21414 from github/redsun82/rerun-slash-command
Add `/rerun` slash command for failed internal checks
2026-03-10 14:01:03 +01:00
Anders Schack-Mulligen
6a6bb5ebf9 Merge pull request #21441 from aschackmull/cfg/switch-sharing
Cfg: Share more code for switch statements.
2026-03-10 13:50:21 +01:00
Florin Coada
15f7a95209 Merge pull request #21440 from github/codeql-spark-run-22877174736
Update changelog documentation site for codeql-cli-2.24.3
2026-03-10 12:21:56 +00:00
dependabot[bot]
b631138b63 Bump rules_shell from 0.5.0 to 0.6.1
Bumps [rules_shell](https://github.com/bazelbuild/rules_shell) from 0.5.0 to 0.6.1.
- [Release notes](https://github.com/bazelbuild/rules_shell/releases)
- [Commits](https://github.com/bazelbuild/rules_shell/compare/v0.5.0...v0.6.1)

---
updated-dependencies:
- dependency-name: rules_shell
  dependency-version: 0.6.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-10 11:21:40 +00:00
dependabot[bot]
093d36ebe6 Bump googletest from 1.14.0.bcr.1 to 1.17.0.bcr.2
Bumps [googletest](https://github.com/google/googletest) from 1.14.0.bcr.1 to 1.17.0.bcr.2.
- [Release notes](https://github.com/google/googletest/releases)
- [Commits](https://github.com/google/googletest/commits)

---
updated-dependencies:
- dependency-name: googletest
  dependency-version: 1.17.0.bcr.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-10 11:21:37 +00:00
dependabot[bot]
c7349740f0 Bump rules_android from 0.6.4 to 0.7.1
Bumps [rules_android](https://github.com/bazelbuild/rules_android) from 0.6.4 to 0.7.1.
- [Release notes](https://github.com/bazelbuild/rules_android/releases)
- [Commits](https://github.com/bazelbuild/rules_android/compare/v0.6.4...v0.7.1)

---
updated-dependencies:
- dependency-name: rules_android
  dependency-version: 0.7.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-10 11:21:34 +00:00
Anders Schack-Mulligen
efa797a21d Update shared/controlflow/codeql/controlflow/ControlFlowGraph.qll
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-10 11:22:15 +01:00
Anders Schack-Mulligen
77d4f5a2dc Cfg: Update fallsThrough default. 2026-03-10 11:10:24 +01:00
Anders Schack-Mulligen
edf88b34da Cfg: Move Case.getBodyElement to shared code. 2026-03-10 11:02:58 +01:00
Owen Mansel-Chan
0215ea3ee3 Merge pull request #21426 from owen-mc/cpp/validate-constructor-summary-models
C++: Add model validation for constructor summary models
2026-03-10 09:42:24 +00:00
Anders Schack-Mulligen
35ac66d3aa Cfg: Move getCaseControlFlowOrder to shared code. 2026-03-10 10:39:32 +01:00
Anders Schack-Mulligen
219fe03637 Merge pull request #21430 from aschackmull/csharp/switch-ast-simplify
C#: Disentangle SwitchStmt AST and CFG.
2026-03-10 10:23:37 +01:00
Mario Campos
b7a5b08d61 Fix formatting issues in codeql-cli-2.23.1.rst 2026-03-09 17:20:34 -05:00
Mario Campos
87ec22db65 Fix formatting of Kotlin version support note 2026-03-09 17:19:56 -05:00
Mario Campos
f52195e96d Fix formatting in Kotlin version support note 2026-03-09 17:19:36 -05:00
Mario Campos
430ed055bc Fix formatting for Kotlin version support note 2026-03-09 17:19:18 -05:00
github-actions[bot]
8e85c4c0ea update codeql documentation 2026-03-09 22:17:15 +00:00
Mario Campos
017822b872 Merge pull request #21439 from github/revert-21438-codeql-spark-run-22872006382
Revert "Update changelog documentation site for codeql-cli-2.24.3"
2026-03-09 17:02:43 -05:00
Mario Campos
eea61ea821 Revert "Update changelog documentation site for codeql-cli-2.24.3" 2026-03-09 16:55:09 -05:00
Jon Janego
b289266398 Merge pull request #21438 from github/codeql-spark-run-22872006382
Update changelog documentation site for codeql-cli-2.24.3
2026-03-09 15:44:14 -05:00
Mario Campos
f5545516db Fix formatting in codeql-cli-2.19.1.rst 2026-03-09 15:34:27 -05:00
Mario Campos
216bc76694 Fix formatting in Kotlin version support note 2026-03-09 15:34:07 -05:00
Mario Campos
5921dacf52 Fix formatting of Kotlin version support note 2026-03-09 15:33:08 -05:00
Mario Campos
ca44c777f0 Fix formatting issues in codeql-cli-2.23.1.rst 2026-03-09 15:32:42 -05:00
github-actions[bot]
5283413055 update codeql documentation 2026-03-09 20:00:48 +00:00
Ian Lynagh
dbb8bb86ba C++: Small simplification 2026-03-09 17:45:38 +00:00
Anders Schack-Mulligen
4013f00b19 C#: Disentangle SwitchStmt AST and CFG. 2026-03-09 15:07:59 +01:00
Owen Mansel-Chan
e0e5319b11 C#: Make corresponding predicate private 2026-03-09 13:44:52 +00:00
Owen Mansel-Chan
d8007a85e6 Java: Make corresponding predicate private 2026-03-09 13:44:50 +00:00
Owen Mansel-Chan
512e27187e Make new predicate private 2026-03-09 13:44:48 +00:00
Taus
c5360ba46c Python: Fix bad join in method call order computation
This join had badness 1127 on the project FiacreT/M-moire, producing ~31
million tuples in order to end up with only ~27k tuples later in the
pipeline. With the fix, we reduce this by roughly the full 31 million
(the new materialised helper predicate accounting for roughly 130k
tuples on its own).

Co-authored-by: Mathias Vorreiter Pedersen <mathiasvp@github.com>
2026-03-09 13:09:29 +00:00
Owen Mansel-Chan
097681e705 Update cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-07 10:17:58 +00:00
Owen Mansel-Chan
63e8061917 Add model validation for constructor summary models 2026-03-07 09:57:09 +00:00
Tom Hvitved
4dca9aa958 Rust: Small refactor in TypeMention.qll 2026-03-06 15:33:11 +01:00
Geoffrey White
da99d3660d C++: Turns out we can simplify. 2026-03-06 11:53:43 +00:00
Geoffrey White
7f6fd34d46 C++: Expose a type resolution issue. 2026-03-06 11:34:57 +00:00
Geoffrey White
d23a3f821e C++: Add a test case for WrongTypeFormatArguments involving code that's included twice. 2026-03-06 11:34:16 +00:00
Paolo Tranquilli
9bf4262dbb Add /rerun slash command for failed internal checks 2026-03-05 11:38:27 +01:00
991 changed files with 6877 additions and 28764 deletions

View File

@@ -45,3 +45,5 @@ updates:
directory: "/"
schedule:
interval: weekly
exclude-paths:
- "misc/bazel/registry/**"

View File

@@ -1,78 +0,0 @@
name: "Compile all queries using the latest stable CodeQL CLI"
on:
push:
branches: # makes sure the cache gets populated - running on the branches people tend to merge into.
- main
- "rc/*"
- "codeql-cli-*"
pull_request:
paths:
- '**.ql'
- '**.qll'
- '**/qlpack.yml'
- '**.dbscheme'
permissions:
contents: read
jobs:
detect-changes:
if: github.repository_owner == 'github'
runs-on: ubuntu-latest
outputs:
languages: ${{ steps.detect.outputs.languages }}
steps:
- uses: actions/checkout@v5
- name: Detect changed languages
id: detect
run: |
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
# For PRs, detect which languages have changes
changed_files=$(gh pr view ${{ github.event.pull_request.number }} --json files --jq '.files.[].path')
languages=()
for lang in actions cpp csharp go java javascript python ql ruby rust swift; do
if echo "$changed_files" | grep -qE "^($lang/|shared/)" ; then
languages+=("$lang")
fi
done
echo "languages=$(jq -c -n '$ARGS.positional' --args "${languages[@]}")" >> $GITHUB_OUTPUT
else
# For pushes to main/rc branches, run all languages
echo 'languages=["actions","cpp","csharp","go","java","javascript","python","ql","ruby","rust","swift"]' >> $GITHUB_OUTPUT
fi
env:
GH_TOKEN: ${{ github.token }}
compile-queries:
needs: detect-changes
if: github.repository_owner == 'github' && needs.detect-changes.outputs.languages != '[]'
runs-on: ubuntu-latest-xl
strategy:
fail-fast: false
matrix:
language: ${{ fromJson(needs.detect-changes.outputs.languages) }}
steps:
- uses: actions/checkout@v5
- name: Setup CodeQL
uses: ./.github/actions/fetch-codeql
with:
channel: 'release'
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
key: ${{ matrix.language }}-queries
- name: check formatting
run: find shared ${{ matrix.language }}/ql -type f \( -name "*.qll" -o -name "*.ql" \) -print0 | xargs -0 -n 3000 -P 10 codeql query format -q --check-only
- name: compile queries - check-only
# run with --check-only if running in a PR (github.sha != main)
if : ${{ github.event_name == 'pull_request' }}
shell: bash
run: codeql query compile -q -j0 ${{ matrix.language }}/ql/{src,examples} --keep-going --warnings=error --check-only --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" --compilation-cache-size=500 --ram=56000
- name: compile queries - full
# do full compile if running on main - this populates the cache
if : ${{ github.event_name != 'pull_request' }}
shell: bash
run: codeql query compile -q -j0 ${{ matrix.language }}/ql/{src,examples} --keep-going --warnings=error --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" --compilation-cache-size=500 --ram=56000

View File

@@ -1,236 +0,0 @@
name: "Ruby: Build"
on:
push:
paths:
- "ruby/**"
- .github/workflows/ruby-build.yml
- .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml
- "shared/tree-sitter-extractor/**"
branches:
- main
- "rc/*"
pull_request:
paths:
- "ruby/**"
- .github/workflows/ruby-build.yml
- .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml
- "shared/tree-sitter-extractor/**"
branches:
- main
- "rc/*"
workflow_dispatch:
inputs:
tag:
description: "Version tag to create"
required: false
env:
CARGO_TERM_COLOR: always
defaults:
run:
working-directory: ruby
permissions:
contents: read
jobs:
build:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
- name: Install GNU tar
if: runner.os == 'macOS'
run: |
brew install gnu-tar
echo "/usr/local/opt/gnu-tar/libexec/gnubin" >> $GITHUB_PATH
- name: Prepare Windows
if: runner.os == 'Windows'
shell: powershell
run: |
git config --global core.longpaths true
- uses: ./.github/actions/os-version
id: os_version
- name: Cache entire extractor
uses: actions/cache@v3
id: cache-extractor
with:
path: |
target/release/codeql-extractor-ruby
target/release/codeql-extractor-ruby.exe
ruby/extractor/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-ruby-extractor-${{ hashFiles('ruby/extractor/rust-toolchain.toml', 'ruby/extractor/Cargo.lock') }}-${{ hashFiles('shared/tree-sitter-extractor') }}-${{ hashFiles('ruby/extractor/**/*.rs') }}
- uses: actions/cache@v3
if: steps.cache-extractor.outputs.cache-hit != 'true'
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-ruby-rust-cargo-${{ hashFiles('ruby/extractor/rust-toolchain.toml', 'ruby/extractor/**/Cargo.lock') }}
- name: Check formatting
if: steps.cache-extractor.outputs.cache-hit != 'true'
run: cd extractor && cargo fmt -- --check
- name: Build
if: steps.cache-extractor.outputs.cache-hit != 'true'
run: cd extractor && cargo build --verbose
- name: Run tests
if: steps.cache-extractor.outputs.cache-hit != 'true'
run: cd extractor && cargo test --verbose
- name: Release build
if: steps.cache-extractor.outputs.cache-hit != 'true'
run: cd extractor && cargo build --release
- name: Generate dbscheme
if: ${{ matrix.os == 'ubuntu-latest' && steps.cache-extractor.outputs.cache-hit != 'true'}}
run: ../target/release/codeql-extractor-ruby generate --dbscheme ql/lib/ruby.dbscheme --library ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
- uses: actions/upload-artifact@v4
if: ${{ matrix.os == 'ubuntu-latest' }}
with:
name: ruby.dbscheme
path: ruby/ql/lib/ruby.dbscheme
- uses: actions/upload-artifact@v4
if: ${{ matrix.os == 'ubuntu-latest' }}
with:
name: TreeSitter.qll
path: ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
- uses: actions/upload-artifact@v4
with:
name: extractor-${{ matrix.os }}
path: |
target/release/codeql-extractor-ruby
target/release/codeql-extractor-ruby.exe
retention-days: 1
compile-queries:
if: github.repository_owner == 'github'
runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v5
- name: Fetch CodeQL
uses: ./.github/actions/fetch-codeql
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
key: ruby-build
- name: Build Query Pack
run: |
PACKS=${{ runner.temp }}/query-packs
rm -rf $PACKS
codeql pack create ../misc/suite-helpers --output "$PACKS"
codeql pack create ../shared/regex --output "$PACKS"
codeql pack create ../shared/ssa --output "$PACKS"
codeql pack create ../shared/tutorial --output "$PACKS"
codeql pack create ql/lib --output "$PACKS"
codeql pack create -j0 ql/src --output "$PACKS" --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
PACK_FOLDER=$(readlink -f "$PACKS"/codeql/ruby-queries/*)
codeql generate query-help --format=sarifv2.1.0 --output="${PACK_FOLDER}/rules.sarif" ql/src
(cd ql/src; find queries \( -name '*.qhelp' -o -name '*.rb' -o -name '*.erb' \) -exec bash -c 'mkdir -p "'"${PACK_FOLDER}"'/$(dirname "{}")"' \; -exec cp "{}" "${PACK_FOLDER}/{}" \;)
- uses: actions/upload-artifact@v4
with:
name: codeql-ruby-queries
path: |
${{ runner.temp }}/query-packs/*
retention-days: 1
include-hidden-files: true
package:
runs-on: ubuntu-latest
needs: [build, compile-queries]
steps:
- uses: actions/checkout@v5
- uses: actions/download-artifact@v4
with:
name: ruby.dbscheme
path: ruby/ruby
- uses: actions/download-artifact@v4
with:
name: extractor-ubuntu-latest
path: ruby/linux64
- uses: actions/download-artifact@v4
with:
name: extractor-windows-latest
path: ruby/win64
- uses: actions/download-artifact@v4
with:
name: extractor-macos-latest
path: ruby/osx64
- run: |
mkdir -p ruby
cp -r codeql-extractor.yml tools ql/lib/ruby.dbscheme.stats ruby/
mkdir -p ruby/tools/{linux64,osx64,win64}
cp linux64/codeql-extractor-ruby ruby/tools/linux64/extractor
cp osx64/codeql-extractor-ruby ruby/tools/osx64/extractor
cp win64/codeql-extractor-ruby.exe ruby/tools/win64/extractor.exe
chmod +x ruby/tools/{linux64,osx64}/extractor
zip -rq codeql-ruby.zip ruby
- uses: actions/upload-artifact@v4
with:
name: codeql-ruby-pack
path: ruby/codeql-ruby.zip
retention-days: 1
include-hidden-files: true
- uses: actions/download-artifact@v4
with:
name: codeql-ruby-queries
path: ruby/qlpacks
- run: |
echo '{
"provide": [
"ruby/codeql-extractor.yml",
"qlpacks/*/*/*/qlpack.yml"
]
}' > .codeqlmanifest.json
zip -rq codeql-ruby-bundle.zip .codeqlmanifest.json ruby qlpacks
- uses: actions/upload-artifact@v4
with:
name: codeql-ruby-bundle
path: ruby/codeql-ruby-bundle.zip
retention-days: 1
include-hidden-files: true
test:
defaults:
run:
working-directory: ${{ github.workspace }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
needs: [package]
steps:
- uses: actions/checkout@v5
- name: Fetch CodeQL
uses: ./.github/actions/fetch-codeql
- name: Download Ruby bundle
uses: actions/download-artifact@v4
with:
name: codeql-ruby-bundle
path: ${{ runner.temp }}
- name: Unzip Ruby bundle
shell: bash
run: unzip -q -d "${{ runner.temp }}/ruby-bundle" "${{ runner.temp }}/codeql-ruby-bundle.zip"
- name: Run QL test
shell: bash
run: |
codeql test run --search-path "${{ runner.temp }}/ruby-bundle" --additional-packs "${{ runner.temp }}/ruby-bundle" ruby/ql/test/library-tests/ast/constants/
- name: Create database
shell: bash
run: |
codeql database create --search-path "${{ runner.temp }}/ruby-bundle" --language ruby --source-root ruby/ql/test/library-tests/ast/constants/ ../database
- name: Analyze database
shell: bash
run: |
codeql database analyze --search-path "${{ runner.temp }}/ruby-bundle" --format=sarifv2.1.0 --output=out.sarif ../database ruby-code-scanning.qls

View File

@@ -1,75 +0,0 @@
name: "Ruby: Collect database stats"
on:
push:
branches:
- main
- "rc/*"
paths:
- ruby/ql/lib/ruby.dbscheme
- .github/workflows/ruby-dataset-measure.yml
pull_request:
branches:
- main
- "rc/*"
paths:
- ruby/ql/lib/ruby.dbscheme
- .github/workflows/ruby-dataset-measure.yml
workflow_dispatch:
permissions:
contents: read
jobs:
measure:
env:
CODEQL_THREADS: 4 # TODO: remove this once it's set by the CLI
strategy:
fail-fast: false
matrix:
repo: [rails/rails, discourse/discourse, spree/spree, ruby/ruby]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: ./.github/actions/fetch-codeql
- uses: ./ruby/actions/create-extractor-pack
- name: Checkout ${{ matrix.repo }}
uses: actions/checkout@v5
with:
repository: ${{ matrix.repo }}
path: ${{ github.workspace }}/repo
- name: Create database
run: |
codeql database create \
--search-path "${{ github.workspace }}" \
--threads 4 \
--language ruby --source-root "${{ github.workspace }}/repo" \
"${{ runner.temp }}/database"
- name: Measure database
run: |
mkdir -p "stats/${{ matrix.repo }}"
codeql dataset measure --threads 4 --output "stats/${{ matrix.repo }}/stats.xml" "${{ runner.temp }}/database/db-ruby"
- uses: actions/upload-artifact@v4
with:
name: measurements-${{ hashFiles('stats/**') }}
path: stats
retention-days: 1
merge:
runs-on: ubuntu-latest
needs: measure
steps:
- uses: actions/checkout@v5
- uses: actions/download-artifact@v4
with:
path: stats
- run: |
python -m pip install --user lxml
find stats -name 'stats.xml' | sort | xargs python ruby/scripts/merge_stats.py --output ruby/ql/lib/ruby.dbscheme.stats --normalise ruby_tokeninfo
- uses: actions/upload-artifact@v4
with:
name: ruby.dbscheme.stats
path: ruby/ql/lib/ruby.dbscheme.stats

View File

@@ -1,40 +0,0 @@
name: "Ruby: Run RTJO Language Tests"
on:
pull_request:
types:
- opened
- synchronize
- reopened
- labeled
env:
CARGO_TERM_COLOR: always
defaults:
run:
working-directory: ruby
permissions:
contents: read
jobs:
qltest-rtjo:
if: "github.repository_owner == 'github' && github.event.label.name == 'Run: RTJO Language Tests'"
runs-on: ubuntu-latest-xl
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v5
- uses: ./.github/actions/fetch-codeql
- uses: ./ruby/actions/create-extractor-pack
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
key: ruby-qltest
- name: Run QL tests
run: |
codeql test run --dynamic-join-order-mode=all --threads=0 --ram 50000 --search-path "${{ github.workspace }}" --check-databases --check-diff-informed --check-undefined-labels --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --consistency-queries ql/consistency-queries ql/test --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
env:
GITHUB_TOKEN: ${{ github.token }}

View File

@@ -1,73 +0,0 @@
name: "Ruby: Run QL Tests"
on:
push:
paths:
- "ruby/**"
- "shared/**"
- .github/workflows/ruby-build.yml
- .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml
branches:
- main
- "rc/*"
pull_request:
paths:
- "ruby/**"
- "shared/**"
- .github/workflows/ruby-qltest.yml
- .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml
branches:
- main
- "rc/*"
env:
CARGO_TERM_COLOR: always
defaults:
run:
working-directory: ruby
permissions:
contents: read
jobs:
qlupgrade:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: ./.github/actions/fetch-codeql
- name: Check DB upgrade scripts
run: |
echo >empty.trap
codeql dataset import -S ql/lib/upgrades/initial/ruby.dbscheme testdb empty.trap
codeql dataset upgrade testdb --additional-packs ql/lib
diff -q testdb/ruby.dbscheme ql/lib/ruby.dbscheme
- name: Check DB downgrade scripts
run: |
echo >empty.trap
rm -rf testdb; codeql dataset import -S ql/lib/ruby.dbscheme testdb empty.trap
codeql resolve upgrades --format=lines --allow-downgrades --additional-packs downgrades \
--dbscheme=ql/lib/ruby.dbscheme --target-dbscheme=downgrades/initial/ruby.dbscheme |
xargs codeql execute upgrades testdb
diff -q testdb/ruby.dbscheme downgrades/initial/ruby.dbscheme
qltest:
if: github.repository_owner == 'github'
runs-on: ubuntu-latest-xl
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v5
- uses: ./.github/actions/fetch-codeql
- uses: ./ruby/actions/create-extractor-pack
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
key: ruby-qltest
- name: Run QL tests
run: |
codeql test run --threads=0 --ram 50000 --search-path "${{ github.workspace }}" --check-databases --check-diff-informed --check-undefined-labels --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --consistency-queries ql/consistency-queries ql/test --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
env:
GITHUB_TOKEN: ${{ github.token }}

View File

@@ -15,22 +15,22 @@ local_path_override(
# see https://registry.bazel.build/ for a list of available packages
bazel_dep(name = "platforms", version = "1.0.0")
bazel_dep(name = "rules_cc", version = "0.2.16")
bazel_dep(name = "rules_go", version = "0.59.0")
bazel_dep(name = "rules_java", version = "9.0.3")
bazel_dep(name = "rules_pkg", version = "1.0.1")
bazel_dep(name = "rules_cc", version = "0.2.17")
bazel_dep(name = "rules_go", version = "0.60.0")
bazel_dep(name = "rules_java", version = "9.6.1")
bazel_dep(name = "rules_pkg", version = "1.2.0")
bazel_dep(name = "rules_nodejs", version = "6.7.3")
bazel_dep(name = "rules_python", version = "1.9.0")
bazel_dep(name = "rules_shell", version = "0.5.0")
bazel_dep(name = "bazel_skylib", version = "1.8.1")
bazel_dep(name = "rules_shell", version = "0.6.1")
bazel_dep(name = "bazel_skylib", version = "1.9.0")
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 = "rules_dotnet", version = "0.21.5-codeql.1")
bazel_dep(name = "googletest", version = "1.14.0.bcr.1")
bazel_dep(name = "rules_rust", version = "0.68.1.codeql.1")
bazel_dep(name = "googletest", version = "1.17.0.bcr.2")
bazel_dep(name = "rules_rust", version = "0.69.0")
bazel_dep(name = "zstd", version = "1.5.7.bcr.1")
bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True)

View File

@@ -1,3 +1,7 @@
## 0.4.30
No user-facing changes.
## 0.4.29
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 0.4.30
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.4.29
lastReleaseVersion: 0.4.30

View File

@@ -1,5 +1,5 @@
name: codeql/actions-all
version: 0.4.30-dev
version: 0.4.31-dev
library: true
warnOnImplicitThis: true
dependencies:

View File

@@ -1,3 +1,7 @@
## 0.6.22
No user-facing changes.
## 0.6.21
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 0.6.22
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.6.21
lastReleaseVersion: 0.6.22

View File

@@ -1,5 +1,5 @@
name: codeql/actions-queries
version: 0.6.22-dev
version: 0.6.23-dev
library: false
warnOnImplicitThis: true
groups: [actions, queries]

View File

@@ -199,6 +199,7 @@ def annotate_as_appropriate(filename, lines):
# as overlay[local?]. It is not clear that these heuristics are exactly what we want,
# but they seem to work well enough for now (as determined by speed and accuracy numbers).
if (filename.endswith("Test.qll") or
re.search(r"go/ql/lib/semmle/go/security/[^/]+[.]qll$", filename.replace(os.sep, "/")) or
((filename.endswith("Query.qll") or filename.endswith("Config.qll")) and
any("implements DataFlow::ConfigSig" in line for line in lines))):
return None

View File

@@ -172,10 +172,6 @@
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintDominance.qll",
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll"
],
"C# ControlFlowReachability": [
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ControlFlowReachability.qll"
],
"C++ ExternalAPIs": [
"cpp/ql/src/Security/CWE/CWE-020/ExternalAPIs.qll",
"cpp/ql/src/Security/CWE/CWE-020/ir/ExternalAPIs.qll"

View File

@@ -52,5 +52,6 @@ ql/cpp/ql/src/Summary/LinesOfUserCode.ql
ql/cpp/ql/src/Telemetry/CompilerErrors.ql
ql/cpp/ql/src/Telemetry/DatabaseQuality.ql
ql/cpp/ql/src/Telemetry/ExtractionMetrics.ql
ql/cpp/ql/src/Telemetry/ExtractorInformation.ql
ql/cpp/ql/src/Telemetry/MissingIncludes.ql
ql/cpp/ql/src/Telemetry/SucceededIncludes.ql

View File

@@ -160,6 +160,7 @@ ql/cpp/ql/src/Summary/LinesOfUserCode.ql
ql/cpp/ql/src/Telemetry/CompilerErrors.ql
ql/cpp/ql/src/Telemetry/DatabaseQuality.ql
ql/cpp/ql/src/Telemetry/ExtractionMetrics.ql
ql/cpp/ql/src/Telemetry/ExtractorInformation.ql
ql/cpp/ql/src/Telemetry/MissingIncludes.ql
ql/cpp/ql/src/Telemetry/SucceededIncludes.ql
ql/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 32.ql

View File

@@ -93,5 +93,6 @@ ql/cpp/ql/src/Summary/LinesOfUserCode.ql
ql/cpp/ql/src/Telemetry/CompilerErrors.ql
ql/cpp/ql/src/Telemetry/DatabaseQuality.ql
ql/cpp/ql/src/Telemetry/ExtractionMetrics.ql
ql/cpp/ql/src/Telemetry/ExtractorInformation.ql
ql/cpp/ql/src/Telemetry/MissingIncludes.ql
ql/cpp/ql/src/Telemetry/SucceededIncludes.ql

View File

@@ -1,3 +1,9 @@
## 8.0.1
### Minor Analysis Improvements
* Inline expectations test comments, which are of the form `// $ tag` or `// $ tag=value`, are now parsed more strictly and will not be recognized if there isn't a space after the `$` symbol.
## 8.0.0
### Breaking Changes

View File

@@ -0,0 +1,4 @@
---
category: feature
---
* Added a class `IndirectUninitializedNode` to represent the indirection of an uninitialized local variable as a dataflow node.

View File

@@ -0,0 +1,5 @@
---
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.

View File

@@ -1,4 +1,5 @@
---
category: minorAnalysis
---
## 8.0.1
### Minor Analysis Improvements
* Inline expectations test comments, which are of the form `// $ tag` or `// $ tag=value`, are now parsed more strictly and will not be recognized if there isn't a space after the `$` symbol.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 8.0.0
lastReleaseVersion: 8.0.1

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-all
version: 8.0.1-dev
version: 8.0.2-dev
groups: cpp
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp

View File

@@ -524,6 +524,12 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
not exists(NewOrNewArrayExpr new | e = new.getAllocatorCall().getArgument(0))
)
}
/**
* Holds if this function has an ambiguous return type, meaning that zero or multiple return
* types for this function are present in the database (this can occur in `build-mode: none`).
*/
predicate hasAmbiguousReturnType() { count(this.getType()) != 1 }
}
pragma[noinline]

View File

@@ -1663,7 +1663,7 @@ private module Cached {
private predicate compares_ge(
ValueNumber test, Operand left, Operand right, int k, boolean isGe, GuardValue value
) {
exists(int onemk | k = 1 - onemk | compares_lt(test, right, left, onemk, isGe, value))
compares_lt(test, right, left, 1 - k, isGe, value)
}
/** Rearrange various simple comparisons into `left < right + k` form. */

View File

@@ -353,12 +353,26 @@ module CsvValidation {
)
}
private string getIncorrectConstructorSummaryOutput() {
exists(string namespace, string type, string name, string output |
type = name or
type = name + "<" + any(string s)
|
summaryModel(namespace, type, _, name, _, _, _, output, _, _, _) and
output.matches("ReturnValue%") and
result =
"Constructor model for " + namespace + "." + type +
" should use `Argument[this]` in the output, not `ReturnValue`."
)
}
/** Holds if some row in a CSV-based flow model appears to contain typos. */
query predicate invalidModelRow(string msg) {
msg =
[
getInvalidModelSignature(), getInvalidModelInput(), getInvalidModelOutput(),
getInvalidModelSubtype(), getInvalidModelColumnCount(), KindVal::getInvalidModelKind()
getInvalidModelSubtype(), getInvalidModelColumnCount(), KindVal::getInvalidModelKind(),
getIncorrectConstructorSummaryOutput()
]
}
}

View File

@@ -6,117 +6,67 @@ private import OverlayXml
/**
* Holds always for the overlay variant and never for the base variant.
* This local predicate is used to define local predicates that behave
* differently for the base and overlay variant.
*/
overlay[local]
predicate isOverlay() { databaseMetadata("isOverlay", "true") }
overlay[local]
private string getLocationFilePath(@location_default loc) {
exists(@file file | locations_default(loc, file, _, _, _, _) | files(file, result))
}
/**
* Gets the file path for an element with a single location.
* Holds if the TRAP file or tag `t` is reachable from source file `sourceFile`
* in the base (isOverlayVariant=false) or overlay (isOverlayVariant=true) variant.
*/
overlay[local]
private string getSingleLocationFilePath(@element e) {
exists(@location_default loc |
var_decls(e, _, _, _, loc)
or
fun_decls(e, _, _, _, loc)
or
type_decls(e, _, loc)
or
namespace_decls(e, _, loc, _)
or
macroinvocations(e, _, loc, _)
or
preprocdirects(e, _, loc)
or
diagnostics(e, _, _, _, _, loc)
or
usings(e, _, loc, _)
or
static_asserts(e, _, _, loc, _)
or
derivations(e, _, _, _, loc)
or
frienddecls(e, _, _, loc)
or
comments(e, _, loc)
or
exprs(e, _, loc)
or
stmts(e, _, loc)
or
initialisers(e, _, _, loc)
or
attributes(e, _, _, _, loc)
or
attribute_args(e, _, _, _, loc)
or
namequalifiers(e, _, _, loc)
or
enumconstants(e, _, _, _, _, loc)
or
type_mentions(e, _, loc, _)
or
lambda_capture(e, _, _, _, _, _, loc)
or
concept_templates(e, _, loc)
|
result = getLocationFilePath(loc)
private predicate locallyReachableTrapOrTag(
boolean isOverlayVariant, string sourceFile, @trap_or_tag t
) {
exists(@source_file sf, @trap trap |
(if isOverlay() then isOverlayVariant = true else isOverlayVariant = false) and
source_file_uses_trap(sf, trap) and
source_file_name(sf, sourceFile) and
(t = trap or trap_uses_tag(trap, t))
)
}
/**
* Gets the file path for an element with potentially multiple locations.
* Holds if element `e` is in TRAP file or tag `t`
* in the base (isOverlayVariant=false) or overlay (isOverlayVariant=true) variant.
*/
overlay[local]
private string getMultiLocationFilePath(@element e) {
exists(@location_default loc |
var_decls(_, e, _, _, loc)
or
fun_decls(_, e, _, _, loc)
or
type_decls(_, e, loc)
or
namespace_decls(_, e, loc, _)
|
result = getLocationFilePath(loc)
)
}
/**
* A local helper predicate that holds in the base variant and never in the
* overlay variant.
*/
overlay[local]
private predicate isBase() { not isOverlay() }
/**
* Holds if `path` was extracted in the overlay database.
*/
overlay[local]
private predicate overlayHasFile(string path) {
isOverlay() and
files(_, path) and
path != ""
private predicate locallyInTrapOrTag(boolean isOverlayVariant, @element e, @trap_or_tag t) {
(if isOverlay() then isOverlayVariant = true else isOverlayVariant = false) and
in_trap_or_tag(e, t)
}
/**
* Discards an element from the base variant if:
* - It has a single location in a file extracted in the overlay, or
* - All of its locations are in files extracted in the overlay.
* - We have knowledge about what TRAP file or tag it is in (in the base).
* - It is not in any overlay TRAP file or tag that is reachable from an overlay source file.
* - For every base TRAP file or tag that contains it and is reachable from a base source file,
* either the source file has changed, or the overlay has redefined the TRAP file or tag,
* or the overlay runner has re-extracted the same source file.
*/
overlay[discard_entity]
private predicate discardElement(@element e) {
isBase() and
(
overlayHasFile(getSingleLocationFilePath(e))
or
forex(string path | path = getMultiLocationFilePath(e) | overlayHasFile(path))
// If we don't have any knowledge about what TRAP file something
// is in, then we don't want to discard it, so we only consider
// entities that are known to be in a base TRAP file or tag.
locallyInTrapOrTag(false, e, _) and
// Anything that is reachable from an overlay source file should
// not be discarded.
not exists(@trap_or_tag t | locallyInTrapOrTag(true, e, t) |
locallyReachableTrapOrTag(true, _, t)
) and
// Finally, we have to make sure the base variant does not retain it.
// If it is reachable from a base source file, then that is
// sufficient unless either the base source file has changed (in
// particular, been deleted), or the overlay has redefined the TRAP
// file or tag it is in, or the overlay runner has re-extracted the same
// source file (e.g. because a header it includes has changed).
forall(@trap_or_tag t, string sourceFile |
locallyInTrapOrTag(false, e, t) and
locallyReachableTrapOrTag(false, sourceFile, t)
|
overlayChangedFiles(sourceFile) or
locallyReachableTrapOrTag(true, _, t) or
locallyReachableTrapOrTag(true, sourceFile, _)
)
}

View File

@@ -321,6 +321,12 @@ module Public {
*/
Operand asIndirectOperand(int index) { hasOperandAndIndex(this, result, index) }
/**
* Gets the instruction that is indirectly tracked by this node behind
* `index` number of indirections.
*/
Instruction asIndirectInstruction(int index) { hasInstructionAndIndex(this, result, index) }
/**
* Holds if this node is at index `i` in basic block `block`.
*
@@ -617,6 +623,25 @@ module Public {
*/
LocalVariable asUninitialized() { result = this.(UninitializedNode).getLocalVariable() }
/**
* Gets the uninitialized local variable corresponding to this node behind
* `index` number of indirections, if any.
*/
LocalVariable asIndirectUninitialized(int index) {
exists(IndirectUninitializedNode indirectUninitializedNode |
this = indirectUninitializedNode and
indirectUninitializedNode.getIndirectionIndex() = index
|
result = indirectUninitializedNode.getLocalVariable()
)
}
/**
* Gets the uninitialized local variable corresponding to this node behind
* a number indirections, if any.
*/
LocalVariable asIndirectUninitialized() { result = this.asIndirectUninitialized(_) }
/**
* Gets the positional parameter corresponding to the node that represents
* the value of the parameter after `index` number of loads, if any. For
@@ -761,16 +786,13 @@ module Public {
final override Type getType() { result = this.getPreUpdateNode().getType() }
}
/**
* The value of an uninitialized local variable, viewed as a node in a data
* flow graph.
*/
class UninitializedNode extends Node {
abstract private class AbstractUninitializedNode extends Node {
LocalVariable v;
int indirectionIndex;
UninitializedNode() {
AbstractUninitializedNode() {
exists(SsaImpl::Definition def, SsaImpl::SourceVariable sv |
def.getIndirectionIndex() = 0 and
def.getIndirectionIndex() = indirectionIndex and
def.getValue().asInstruction() instanceof UninitializedInstruction and
SsaImpl::defToNode(this, def, sv) and
v = sv.getBaseVariable().(SsaImpl::BaseIRVariable).getIRVariable().getAst()
@@ -781,6 +803,25 @@ module Public {
LocalVariable getLocalVariable() { result = v }
}
/**
* The value of an uninitialized local variable, viewed as a node in a data
* flow graph.
*/
class UninitializedNode extends AbstractUninitializedNode {
UninitializedNode() { indirectionIndex = 0 }
}
/**
* The value of an uninitialized local variable behind one or more levels of
* indirection, viewed as a node in a data flow graph.
*/
class IndirectUninitializedNode extends AbstractUninitializedNode {
IndirectUninitializedNode() { indirectionIndex > 0 }
/** Gets the indirection index of this node. */
int getIndirectionIndex() { result = indirectionIndex }
}
/**
* The value of a parameter at function entry, viewed as a node in a data
* flow graph. This includes both explicit parameters such as `x` in `f(x)`
@@ -795,6 +836,12 @@ module Public {
/** An explicit positional parameter, including `this`, but not `...`. */
final class DirectParameterNode = AbstractDirectParameterNode;
/**
* A node representing an indirection of a positional parameter,
* including `*this`, but not `*...`.
*/
final class IndirectParameterNode = AbstractIndirectParameterNode;
final class ExplicitParameterNode = AbstractExplicitParameterNode;
/** An implicit `this` parameter. */
@@ -954,11 +1001,6 @@ module Public {
private import Public
/**
* A node representing an indirection of a parameter.
*/
final class IndirectParameterNode = AbstractIndirectParameterNode;
/**
* A class that lifts pre-SSA dataflow nodes to regular dataflow nodes.
*/

View File

@@ -1,3 +1,7 @@
## 1.5.13
No user-facing changes.
## 1.5.12
No user-facing changes.

View File

@@ -218,7 +218,9 @@ where
// only report if we cannot prove that the result of the
// multiplication will be less (resp. greater) than the
// maximum (resp. minimum) number we can compute.
overflows(me, t1)
overflows(me, t1) and
// exclude cases where the expression type may not have been extracted accurately
not me.getParent().(Call).getTarget().hasAmbiguousReturnType()
select me,
"Multiplication result may overflow '" + me.getType().toString() + "' before it is converted to '"
+ me.getFullyConverted().getType().toString() + "'."

View File

@@ -168,9 +168,11 @@ where
formatOtherArgType(ffc, n, expected, arg, actual) and
not actual.getUnspecifiedType().(IntegralType).getSize() = sizeof_IntType()
) and
// Exclude some cases where we're less confident the result is correct / clear / valuable
not arg.isAffectedByMacro() and
not arg.isFromUninstantiatedTemplate(_) and
not actual.stripType() instanceof ErroneousType and
not arg.getType().stripType().(RoutineType).getReturnType() instanceof ErroneousType and
not arg.(Call).mayBeFromImplicitlyDeclaredFunction() and
// Make sure that the format function definition is consistent
count(ffc.getTarget().getFormatParameterIndex()) = 1

View File

@@ -4,7 +4,7 @@
* allows for a cross-site scripting vulnerability.
* @kind path-problem
* @problem.severity error
* @security-severity 6.1
* @security-severity 7.8
* @precision high
* @id cpp/cgi-xss
* @tags security

View File

@@ -23,13 +23,31 @@ import Flow::PathGraph
predicate isSource(FlowSource source, string sourceType) { sourceType = source.getSourceType() }
/**
* Holds if `f` is a printf-like function or a (possibly nested) wrapper
* that forwards a format-string parameter to one.
*
* Functions that *implement* printf-like behavior (e.g. a custom
* `vsnprintf` variant) internally parse the caller-supplied format string
* and build small, bounded, local format strings such as `"%d"` or `"%ld"`
* for inner `sprintf` calls. Taint that reaches those inner calls via the
* parsed format specifier is not exploitable, so sinks inside such
* functions should be excluded.
*/
private predicate isPrintfImplementation(Function f) {
f instanceof PrintfLikeFunction
or
exists(PrintfLikeFunction printf | printf.wrapperFunction(f, _, _))
}
module Config implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) { isSource(node, _) }
predicate isSink(DataFlow::Node node) {
exists(PrintfLikeFunction printf |
printf.outermostWrapperFunctionCall([node.asExpr(), node.asIndirectExpr()], _)
)
) and
not isPrintfImplementation([node.asExpr(), node.asIndirectExpr()].getEnclosingFunction())
}
private predicate isArithmeticNonCharType(ArithmeticType type) {

View File

@@ -18,7 +18,8 @@ import IncorrectPointerScalingCommon
private predicate isCharSzPtrExpr(Expr e) {
exists(PointerType pt | pt = e.getFullyConverted().getUnspecifiedType() |
pt.getBaseType() instanceof CharType or
pt.getBaseType() instanceof VoidType
pt.getBaseType() instanceof VoidType or
pt.getBaseType() instanceof ErroneousType // this could be char / void type in a successful compilation
)
}

View File

@@ -0,0 +1,25 @@
import cpp
import codeql.util.ReportStats
module CallTargetStats implements StatsSig {
private class RelevantCall extends Call {
RelevantCall() { this.getFile() = any(File f | f.fromSource() and exists(f.getRelativePath())) }
}
// We assume that calls with an implicit target are calls that could not be
// resolved. This is accurate in the vast majority of cases, but is inaccurate
// for calls that deliberately rely on implicitly declared functions.
private predicate hasImplicitTarget(RelevantCall call) {
call.getTarget().getADeclarationEntry().isImplicit()
}
int getNumberOfOk() { result = count(RelevantCall call | not hasImplicitTarget(call)) }
int getNumberOfNotOk() { result = count(RelevantCall call | hasImplicitTarget(call)) }
string getOkText() { result = "calls with call target" }
string getNotOkText() { result = "calls with missing call target" }
}
module CallTargetStatsReport = ReportStats<CallTargetStats>;

View File

@@ -0,0 +1,25 @@
/**
* @name C/C++ extraction information
* @description Information about the extraction for a C/C++ database
* @kind metric
* @tags summary telemetry
* @id cpp/telemetry/extraction-information
*/
import cpp
import DatabaseQuality
from string key, float value
where
(
CallTargetStatsReport::numberOfOk(key, value) or
CallTargetStatsReport::numberOfNotOk(key, value) or
CallTargetStatsReport::percentageOfOk(key, value)
) and
/* Infinity */
value != 1.0 / 0.0 and
/* -Infinity */
value != -1.0 / 0.0 and
/* NaN */
value != 0.0 / 0.0
select key, value

View File

@@ -0,0 +1,4 @@
---
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.

View File

@@ -0,0 +1,4 @@
---
category: queryMetadata
---
* The `@security-severity` metadata of `cpp/cgi-xss` has been increased from 6.1 (medium) to 7.8 (high).

View File

@@ -0,0 +1,4 @@
---
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.

View File

@@ -0,0 +1,4 @@
---
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.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Fixed an issue with the "Uncontrolled format string" (`cpp/tainted-format-string`) query involving certain kinds of formatting function implementations.

View File

@@ -0,0 +1,3 @@
## 1.5.13
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.5.12
lastReleaseVersion: 1.5.13

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-queries
version: 1.5.13-dev
version: 1.5.14-dev
groups:
- cpp
- queries

View File

@@ -0,0 +1,28 @@
// semmle-extractor-options: --expect_errors
void test_float_double1(float f, double d) {
float r1 = f * f; // GOOD
float r2 = f * d; // GOOD
double r3 = f * f; // BAD
double r4 = f * d; // GOOD
float f1 = fabsf(f * f); // GOOD
float f2 = fabsf(f * d); // GOOD
double f3 = fabs(f * f); // BAD [NOT DETECTED]
double f4 = fabs(f * d); // GOOD
}
double fabs(double f);
float fabsf(float f);
void test_float_double2(float f, double d) {
float r1 = f * f; // GOOD
float r2 = f * d; // GOOD
double r3 = f * f; // BAD
double r4 = f * d; // GOOD
float f1 = fabsf(f * f); // GOOD
float f2 = fabsf(f * d); // GOOD
double f3 = fabs(f * f); // BAD [NOT DETECTED]
double f4 = fabs(f * d); // GOOD
}

View File

@@ -1,3 +1,5 @@
| Buildless.c:6:17:6:21 | ... * ... | Multiplication result may overflow 'float' before it is converted to 'double'. |
| Buildless.c:21:17:21:21 | ... * ... | Multiplication result may overflow 'float' before it is converted to 'double'. |
| IntMultToLong.c:4:10:4:14 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'long long'. |
| IntMultToLong.c:7:16:7:20 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'long long'. |
| IntMultToLong.c:18:19:18:23 | ... * ... | Multiplication result may overflow 'float' before it is converted to 'double'. |

View File

@@ -1 +1,3 @@
| second.cpp:26:18:26:39 | ... - ... | This format specifier for type 'int' does not match the argument type 'long'. |
| second.cpp:29:18:29:39 | ... - ... | This format specifier for type 'unsigned int' does not match the argument type 'long'. |
| tests.c:7:18:7:18 | 1 | This format specifier for type 'char *' does not match the argument type 'int'. |

View File

@@ -0,0 +1,3 @@
// defines type size_t plausibly
typedef unsigned long size_t;

View File

@@ -0,0 +1,32 @@
// semmle-extractor-options: --expect_errors
int printf(const char * format, ...);
// defines type `myFunctionPointerType`, referencing `size_t`
typedef size_t (*myFunctionPointerType) ();
void test_size_t() {
size_t s = 0;
printf("%zd", s); // GOOD
printf("%zi", s); // GOOD
printf("%zu", s); // GOOD (we generally permit signedness changes)
printf("%zx", s); // GOOD (we generally permit signedness changes)
printf("%d", s); // BAD [NOT DETECTED]
printf("%ld", s); // DUBIOUS [NOT DETECTED]
printf("%lld", s); // DUBIOUS [NOT DETECTED]
printf("%u", s); // BAD [NOT DETECTED]
char buffer[1024];
printf("%zd", &buffer[1023] - buffer); // GOOD
printf("%zi", &buffer[1023] - buffer); // GOOD
printf("%zu", &buffer[1023] - buffer); // GOOD
printf("%zx", &buffer[1023] - buffer); // GOOD
printf("%d", &buffer[1023] - buffer); // BAD
printf("%ld", &buffer[1023] - buffer); // DUBIOUS [NOT DETECTED]
printf("%lld", &buffer[1023] - buffer); // DUBIOUS [NOT DETECTED]
printf("%u", &buffer[1023] - buffer); // BAD
// (for the `%ld` and `%lld` cases, the signedness and type sizes match, `%zd` would be most correct
// and robust but the developer may know enough to make this safe)
}

View File

@@ -1,3 +1,5 @@
| buildless.cpp:5:15:5:25 | sizeof(int) | Suspicious sizeof offset in a pointer arithmetic expression. The type of the pointer is $@. | file://:0:0:0:0 | const short * | const short * |
| buildless.cpp:6:13:6:23 | sizeof(int) | Suspicious sizeof offset in a pointer arithmetic expression. The type of the pointer is $@. | file://:0:0:0:0 | const int * | const int * |
| test.cpp:6:30:6:40 | sizeof(int) | Suspicious sizeof offset in a pointer arithmetic expression. The type of the pointer is $@. | file://:0:0:0:0 | int * | int * |
| test.cpp:14:30:14:40 | sizeof(int) | Suspicious sizeof offset in a pointer arithmetic expression. The type of the pointer is $@. | file://:0:0:0:0 | int * | int * |
| test.cpp:22:25:22:35 | sizeof(int) | Suspicious sizeof offset in a pointer arithmetic expression. The type of the pointer is $@. | file://:0:0:0:0 | int * | int * |

View File

@@ -0,0 +1,10 @@
// semmle-extractor-options: --expect_errors
void test_buildless(const char *p_c, const short *p_short, const int *p_int, const uint8_t *p_8, const uint16_t *p_16, const uint32_t *p_32) {
*(p_c + sizeof(int)); // GOOD (`sizeof(char)` is 1)
*(p_short + sizeof(int)); // BAD
*(p_int + sizeof(int)); // BAD
*(p_8 + sizeof(int)); // GOOD (`sizeof(uint8_t)` is 1, but there's an error in the type)
*(p_16 + sizeof(int)); // BAD [NOT DETECTED]
*(p_32 + sizeof(int)); // BAD [NOT DETECTED]
}

View File

@@ -93,3 +93,9 @@ private:
myChar * const myCharsPointer;
myInt * const myIntsPointer;
};
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
void test_buildless(const char *p_c, const short *p_short, const int *p_int, const uint8_t *p_8, const uint16_t *p_16, const uint32_t *p_32);

View File

@@ -1,3 +1,7 @@
## 1.7.61
No user-facing changes.
## 1.7.60
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.7.61
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.7.60
lastReleaseVersion: 1.7.61

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-all
version: 1.7.61-dev
version: 1.7.62-dev
groups:
- csharp
- solorigate

View File

@@ -1,3 +1,7 @@
## 1.7.61
No user-facing changes.
## 1.7.60
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.7.61
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.7.60
lastReleaseVersion: 1.7.61

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-queries
version: 1.7.61-dev
version: 1.7.62-dev
groups:
- csharp
- solorigate

View File

@@ -1,63 +1,5 @@
import csharp
import semmle.code.csharp.controlflow.internal.Completion
import semmle.code.csharp.controlflow.internal.PreBasicBlocks
import ControlFlow
import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl::Consistency
import semmle.code.csharp.controlflow.internal.Splitting
private predicate splitBB(ControlFlow::BasicBlock bb) {
exists(ControlFlow::Node first |
first = bb.getFirstNode() and
first.isJoin() and
strictcount(first.getAPredecessor().getAstNode()) = 1
)
}
private class RelevantBasicBlock extends ControlFlow::BasicBlock {
RelevantBasicBlock() { not splitBB(this) }
}
predicate bbStartInconsistency(ControlFlowElement cfe) {
exists(RelevantBasicBlock bb | bb.getFirstNode() = cfe.getAControlFlowNode()) and
not cfe = any(PreBasicBlock bb).getFirstElement()
}
predicate bbSuccInconsistency(ControlFlowElement pred, ControlFlowElement succ) {
exists(RelevantBasicBlock predBB, RelevantBasicBlock succBB |
predBB.getLastNode() = pred.getAControlFlowNode() and
succBB = predBB.getASuccessor() and
succBB.getFirstNode() = succ.getAControlFlowNode()
) and
not exists(PreBasicBlock predBB, PreBasicBlock succBB |
predBB.getLastNode() = pred and
succBB = predBB.getASuccessor() and
succBB.getFirstElement() = succ
)
}
predicate bbIntraSuccInconsistency(ControlFlowElement pred, ControlFlowElement succ) {
exists(ControlFlow::BasicBlock bb, int i |
pred.getAControlFlowNode() = bb.getNode(i) and
succ.getAControlFlowNode() = bb.getNode(i + 1)
) and
not exists(PreBasicBlock bb |
bb.getLastNode() = pred and
bb.getASuccessor().getFirstElement() = succ
) and
not exists(PreBasicBlock bb, int i |
bb.getNode(i) = pred and
bb.getNode(i + 1) = succ
)
}
query predicate preBasicBlockConsistency(ControlFlowElement cfe1, ControlFlowElement cfe2, string s) {
bbStartInconsistency(cfe1) and
cfe2 = cfe1 and
s = "start inconsistency"
or
bbSuccInconsistency(cfe1, cfe2) and
s = "succ inconsistency"
or
bbIntraSuccInconsistency(cfe1, cfe2) and
s = "intra succ inconsistency"
}

View File

@@ -35,9 +35,7 @@ private module Input implements InputSig<Location, CsharpDataFlow> {
or
n.asExpr().(ObjectCreation).hasInitializer()
or
exists(
n.(PostUpdateNode).getPreUpdateNode().asExprAtNode(LocalFlow::getPostUpdateReverseStep(_))
)
n.(PostUpdateNode).getPreUpdateNode().asExpr() = LocalFlow::getPostUpdateReverseStep(_)
}
predicate argHasPostUpdateExclude(ArgumentNode n) {

View File

@@ -1,3 +1,13 @@
## 5.4.9
### Minor Analysis Improvements
* Inline expectations test comments, which are of the form `// $ tag` or `// $ tag=value`, are now parsed more strictly and will not be recognized if there isn't a space after the `$` symbol.
* Added `System.Net.WebSockets::ReceiveAsync` as a remote flow source.
* Added reverse taint flow from implicit conversion operator calls to their arguments.
* Added post-update nodes for struct-type arguments, allowing data flow out of method calls via those arguments.
* C# 14: Added support for partial constructors.
## 5.4.8
### Minor Analysis Improvements

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* C# 14: Added support for partial constructors.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Added post-update nodes for struct-type arguments, allowing data flow out of method calls via those arguments.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Added reverse taint flow from implicit conversion operator calls to their arguments.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Added `System.Net.WebSockets::ReceiveAsync` as a remote flow source.

View File

@@ -0,0 +1,7 @@
---
category: minorAnalysis
---
* 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.

View File

@@ -0,0 +1,9 @@
## 5.4.9
### Minor Analysis Improvements
* Inline expectations test comments, which are of the form `// $ tag` or `// $ tag=value`, are now parsed more strictly and will not be recognized if there isn't a space after the `$` symbol.
* Added `System.Net.WebSockets::ReceiveAsync` as a remote flow source.
* Added reverse taint flow from implicit conversion operator calls to their arguments.
* Added post-update nodes for struct-type arguments, allowing data flow out of method calls via those arguments.
* C# 14: Added support for partial constructors.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 5.4.8
lastReleaseVersion: 5.4.9

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-all
version: 5.4.9-dev
version: 5.4.10-dev
groups: csharp
dbscheme: semmlecode.csharp.dbscheme
extractor: csharp

View File

@@ -336,6 +336,22 @@ class ExtensionTypeExtensionMethod extends ExtensionMethodImpl {
ExtensionTypeExtensionMethod() { this.isInExtension() }
}
/**
* A non-static member with an initializer, for example a field `int Field = 0`.
*/
private class InitializedInstanceMember extends Member {
private AssignExpr ae;
InitializedInstanceMember() {
not this.isStatic() and
expr_parent_top_level(ae, _, this) and
not ae = getExpressionBody(_)
}
/** Gets the initializer expression. */
AssignExpr getInitializer() { result = ae }
}
/**
* An object initializer method.
*
@@ -347,6 +363,17 @@ class ExtensionTypeExtensionMethod extends ExtensionMethodImpl {
*/
class ObjectInitMethod extends Method {
ObjectInitMethod() { this.getName() = "<object initializer>" }
/**
* Holds if this object initializer method performs the initialization
* of a member via assignment `init`.
*/
predicate initializes(AssignExpr init) {
exists(InitializedInstanceMember m |
this.getDeclaringType().getAMember() = m and
init = m.getInitializer()
)
}
}
/**

View File

@@ -214,6 +214,8 @@ private module Cached {
parent*(enclosingStart(cfe), c.(Constructor).getInitializer())
or
parent*(cfe, c.(Constructor).getObjectInitializerCall())
or
parent*(cfe, any(AssignExpr init | c.(ObjectInitMethod).initializes(init)))
}
/** Holds if the enclosing statement of expression `e` is `s`. */

View File

@@ -183,9 +183,10 @@ class SwitchStmt extends SelectionStmt, Switch, @switch_stmt {
* return 3;
* }
* ```
* Note that this reorders the `default` case to always be at the end.
*/
override CaseStmt getCase(int i) { result = SwithStmtInternal::getCase(this, i) }
override CaseStmt getCase(int i) {
result = rank[i + 1](CaseStmt cs, int idx | cs = this.getChildStmt(idx) | cs order by idx)
}
/** Gets a case of this `switch` statement. */
override CaseStmt getACase() { result = this.getCase(_) }
@@ -208,87 +209,29 @@ class SwitchStmt extends SelectionStmt, Switch, @switch_stmt {
* ```csharp
* switch (x) {
* case "abc": // i = 0
* return 0;
* case int i when i > 0: // i = 1
* return 1;
* case string s: // i = 2
* Console.WriteLine(s);
* return 2; // i = 3
* default: // i = 4
* return 3; // i = 5
* return 0; // i = 1
* case int i when i > 0: // i = 2
* return 1; // i = 3
* case string s: // i = 4
* Console.WriteLine(s); // i = 5
* return 2; // i = 6
* default: // i = 7
* return 3; // i = 8
* }
* ```
*
* Note that each non-`default` case is a labeled statement, so the statement
* that follows is a child of the labeled statement, and not the `switch` block.
*/
Stmt getStmt(int i) { result = SwithStmtInternal::getStmt(this, i) }
Stmt getStmt(int i) { result = this.getChildStmt(i) }
/** Gets a statement in the body of this `switch` statement. */
Stmt getAStmt() { result = this.getStmt(_) }
}
cached
private module SwithStmtInternal {
cached
CaseStmt getCase(SwitchStmt ss, int i) {
exists(int index, int rankIndex |
caseIndex(ss, result, index) and
rankIndex = i + 1 and
index = rank[rankIndex](int j, CaseStmt cs | caseIndex(ss, cs, j) | j)
)
}
/** Implicitly reorder case statements to put the default case last if needed. */
private predicate caseIndex(SwitchStmt ss, CaseStmt case, int index) {
exists(int i | case = ss.getChildStmt(i) |
if case instanceof DefaultCase
then index = max(int j | exists(ss.getChildStmt(j))) + 1
else index = i
)
}
cached
Stmt getStmt(SwitchStmt ss, int i) {
exists(int index, int rankIndex |
result = ss.getChildStmt(index) and
rankIndex = i + 1 and
index =
rank[rankIndex](int j, Stmt s |
// `getChild` includes both labeled statements and the targeted
// statements of labeled statement as separate children, but we
// only want the labeled statement
s = getLabeledStmt(ss, j)
|
j
)
)
}
private Stmt getLabeledStmt(SwitchStmt ss, int i) {
result = ss.getChildStmt(i) and
not result = any(CaseStmt cs).getBody()
}
}
/** A `case` statement. */
class CaseStmt extends Case, @case_stmt {
override Expr getExpr() { result = any(SwitchStmt ss | ss.getACase() = this).getExpr() }
override PatternExpr getPattern() { result = this.getChild(0) }
override Stmt getBody() {
exists(int i, Stmt next |
this = this.getParent().getChild(i) and
next = this.getParent().getChild(i + 1)
|
result = next and
not result instanceof CaseStmt
or
result = next.(CaseStmt).getBody()
)
}
/**
* Gets the condition on this case, if any. For example, the type case on line 3
* has no condition, and the type case on line 4 has condition `s.Length > 0`, in

View File

@@ -10,42 +10,15 @@ private import semmle.code.csharp.ExprOrStmtParent
private import semmle.code.csharp.commons.Compilation
private module Initializers {
/**
* A non-static member with an initializer, for example a field `int Field = 0`.
*/
class InitializedInstanceMember extends Member {
private AssignExpr ae;
InitializedInstanceMember() {
not this.isStatic() and
expr_parent_top_level(ae, _, this) and
not ae = any(Callable c).getExpressionBody()
}
/** Gets the initializer expression. */
AssignExpr getInitializer() { result = ae }
}
/**
* Holds if `obinit` is an object initializer method that performs the initialization
* of a member via assignment `init`.
*/
predicate obinitInitializes(ObjectInitMethod obinit, AssignExpr init) {
exists(InitializedInstanceMember m |
obinit.getDeclaringType().getAMember() = m and
init = m.getInitializer()
)
}
/**
* Gets the `i`th member initializer expression for object initializer method `obinit`
* in compilation `comp`.
*/
AssignExpr initializedInstanceMemberOrder(ObjectInitMethod obinit, CompilationExt comp, int i) {
obinitInitializes(obinit, result) and
obinit.initializes(result) and
result =
rank[i + 1](AssignExpr ae0, Location l |
obinitInitializes(obinit, ae0) and
obinit.initializes(ae0) and
l = ae0.getLocation() and
getCompilation(l.getFile()) = comp
|
@@ -74,7 +47,7 @@ class CfgScope extends Element, @top_level_exprorstmt_parent {
any(Callable c |
c.(Constructor).hasInitializer()
or
Initializers::obinitInitializes(c, _)
c.(ObjectInitMethod).initializes(_)
or
c.hasBody()
)
@@ -308,6 +281,93 @@ private class ConstructorTree extends ControlFlowTree instanceof Constructor {
}
}
cached
private module SwithStmtInternal {
// Reorders default to be last if needed
cached
CaseStmt getCase(SwitchStmt ss, int i) {
exists(int index, int rankIndex |
caseIndex(ss, result, index) and
rankIndex = i + 1 and
index = rank[rankIndex](int j, CaseStmt cs | caseIndex(ss, cs, j) | j)
)
}
/** Implicitly reorder case statements to put the default case last if needed. */
private predicate caseIndex(SwitchStmt ss, CaseStmt case, int index) {
exists(int i | case = ss.getChildStmt(i) |
if case instanceof DefaultCase
then index = max(int j | exists(ss.getChildStmt(j))) + 1
else index = i
)
}
/**
* Gets the `i`th statement in the body of this `switch` statement.
*
* Example:
*
* ```csharp
* switch (x) {
* case "abc": // i = 0
* return 0;
* case int i when i > 0: // i = 1
* return 1;
* case string s: // i = 2
* Console.WriteLine(s);
* return 2; // i = 3
* default: // i = 4
* return 3; // i = 5
* }
* ```
*
* Note that each non-`default` case is a labeled statement, so the statement
* that follows is a child of the labeled statement, and not the `switch` block.
*/
cached
Stmt getStmt(SwitchStmt ss, int i) {
exists(int index, int rankIndex |
result = ss.getChildStmt(index) and
rankIndex = i + 1 and
index =
rank[rankIndex](int j, Stmt s |
// `getChild` includes both labeled statements and the targeted
// statements of labeled statement as separate children, but we
// only want the labeled statement
s = getLabeledStmt(ss, j)
|
j
)
)
}
private Stmt getLabeledStmt(SwitchStmt ss, int i) {
result = ss.getChildStmt(i) and
not result = caseStmtGetBody(_)
}
}
private ControlFlowElement caseGetBody(Case c) {
result = c.getBody() or result = caseStmtGetBody(c)
}
private ControlFlowElement caseStmtGetBody(CaseStmt c) {
exists(int i, Stmt next |
c = c.getParent().getChild(i) and
next = c.getParent().getChild(i + 1)
|
result = next and
not result instanceof CaseStmt
or
result = caseStmtGetBody(next)
)
}
// Reorders default to be last if needed
private Case switchGetCase(Switch s, int i) {
result = s.(SwitchExpr).getCase(i) or result = SwithStmtInternal::getCase(s, i)
}
abstract private class SwitchTree extends ControlFlowTree instanceof Switch {
override predicate propagatesAbnormal(AstNode child) { child = super.getExpr() }
@@ -315,27 +375,27 @@ abstract private class SwitchTree extends ControlFlowTree instanceof Switch {
// Flow from last element of switch expression to first element of first case
last(super.getExpr(), pred, c) and
c instanceof NormalCompletion and
first(super.getCase(0), succ)
first(switchGetCase(this, 0), succ)
or
// Flow from last element of case pattern to next case
exists(Case case, int i | case = super.getCase(i) |
exists(Case case, int i | case = switchGetCase(this, i) |
last(case.getPattern(), pred, c) and
c.(MatchingCompletion).isNonMatch() and
first(super.getCase(i + 1), succ)
first(switchGetCase(this, i + 1), succ)
)
or
// Flow from last element of condition to next case
exists(Case case, int i | case = super.getCase(i) |
exists(Case case, int i | case = switchGetCase(this, i) |
last(case.getCondition(), pred, c) and
c instanceof FalseCompletion and
first(super.getCase(i + 1), succ)
first(switchGetCase(this, i + 1), succ)
)
}
}
abstract private class CaseTree extends ControlFlowTree instanceof Case {
final override predicate propagatesAbnormal(AstNode child) {
child in [super.getPattern().(ControlFlowElement), super.getCondition(), super.getBody()]
child in [super.getPattern().(ControlFlowElement), super.getCondition(), caseGetBody(this)]
}
override predicate succ(AstNode pred, AstNode succ, Completion c) {
@@ -348,13 +408,13 @@ abstract private class CaseTree extends ControlFlowTree instanceof Case {
first(super.getCondition(), succ)
else
// Flow from last element of pattern to first element of body
first(super.getBody(), succ)
first(caseGetBody(this), succ)
)
or
// Flow from last element of condition to first element of body
last(super.getCondition(), pred, c) and
c instanceof TrueCompletion and
first(super.getBody(), succ)
first(caseGetBody(this), succ)
}
}
@@ -1226,10 +1286,11 @@ module Statements {
c instanceof NormalCompletion
or
// A statement exits with a `break` completion
last(super.getStmt(_), last, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion())
last(SwithStmtInternal::getStmt(this, _), last,
c.(NestedBreakCompletion).getAnInnerCompatibleCompletion())
or
// A statement exits abnormally
last(super.getStmt(_), last, c) and
last(SwithStmtInternal::getStmt(this, _), last, c) and
not c instanceof BreakCompletion and
not c instanceof NormalCompletion and
not any(LabeledStmtTree t |
@@ -1238,8 +1299,8 @@ module Statements {
or
// Last case exits with a non-match
exists(CaseStmt cs, int last_ |
last_ = max(int i | exists(super.getCase(i))) and
cs = super.getCase(last_)
last_ = max(int i | exists(SwithStmtInternal::getCase(this, i))) and
cs = SwithStmtInternal::getCase(this, last_)
|
last(cs.getPattern(), last, c) and
not c.(MatchingCompletion).isMatch()
@@ -1258,22 +1319,22 @@ module Statements {
c instanceof SimpleCompletion
or
// Flow from last element of non-`case` statement `i` to first element of statement `i+1`
exists(int i | last(super.getStmt(i), pred, c) |
not super.getStmt(i) instanceof CaseStmt and
exists(int i | last(SwithStmtInternal::getStmt(this, i), pred, c) |
not SwithStmtInternal::getStmt(this, i) instanceof CaseStmt and
c instanceof NormalCompletion and
first(super.getStmt(i + 1), succ)
first(SwithStmtInternal::getStmt(this, i + 1), succ)
)
or
// Flow from last element of `case` statement `i` to first element of statement `i+1`
exists(int i, Stmt body |
body = super.getStmt(i).(CaseStmt).getBody() and
body = caseStmtGetBody(SwithStmtInternal::getStmt(this, i)) and
// in case of fall-through cases, make sure to not jump from their shared body back
// to one of the fall-through cases
not body = super.getStmt(i + 1).(CaseStmt).getBody() and
not body = caseStmtGetBody(SwithStmtInternal::getStmt(this, i + 1)) and
last(body, pred, c)
|
c instanceof NormalCompletion and
first(super.getStmt(i + 1), succ)
first(SwithStmtInternal::getStmt(this, i + 1), succ)
)
}
}
@@ -1289,7 +1350,7 @@ module Statements {
not c.(MatchingCompletion).isMatch()
or
// Case body exits with any completion
last(super.getBody(), last, c)
last(caseStmtGetBody(this), last, c)
}
final override predicate succ(AstNode pred, AstNode succ, Completion c) {

View File

@@ -1,175 +0,0 @@
/**
* INTERNAL: Do not use.
*
* Provides a basic block implementation on control flow elements. That is,
* a "pre-CFG" where the nodes are (unsplit) control flow elements and the
* successor relation is `succ = succ(pred, _)`.
*
* The logic is duplicated from the implementation in `BasicBlocks.qll`, and
* being an internal class, all predicate documentation has been removed.
*/
import csharp
private import Completion
private import ControlFlowGraphImpl
private import semmle.code.csharp.controlflow.ControlFlowGraph::ControlFlow as Cfg
private import codeql.controlflow.BasicBlock as BB
private predicate startsBB(ControlFlowElement cfe) {
not succ(_, cfe, _) and
(
succ(cfe, _, _)
or
scopeLast(_, cfe, _)
)
or
strictcount(ControlFlowElement pred, Completion c | succ(pred, cfe, c)) > 1
or
succ(_, cfe, any(ConditionalCompletion c))
or
exists(ControlFlowElement pred, int i |
succ(pred, cfe, _) and
i = count(ControlFlowElement succ, Completion c | succ(pred, succ, c))
|
i > 1
or
i = 1 and
scopeLast(_, pred, _)
)
}
private predicate intraBBSucc(ControlFlowElement pred, ControlFlowElement succ) {
succ(pred, succ, _) and
not startsBB(succ)
}
private predicate bbIndex(ControlFlowElement bbStart, ControlFlowElement cfe, int i) =
shortestDistances(startsBB/1, intraBBSucc/2)(bbStart, cfe, i)
private predicate succBB(PreBasicBlock pred, PreBasicBlock succ) { succ = pred.getASuccessor() }
private predicate entryBB(PreBasicBlock bb) { scopeFirst(_, bb) }
private predicate bbIDominates(PreBasicBlock dom, PreBasicBlock bb) =
idominance(entryBB/1, succBB/2)(_, dom, bb)
class PreBasicBlock extends ControlFlowElement {
PreBasicBlock() { startsBB(this) }
PreBasicBlock getASuccessor(Cfg::SuccessorType t) {
succ(this.getLastNode(), result, any(Completion c | t = c.getAMatchingSuccessorType()))
}
deprecated PreBasicBlock getASuccessorByType(Cfg::SuccessorType t) {
result = this.getASuccessor(t)
}
PreBasicBlock getASuccessor() { result = this.getASuccessor(_) }
PreBasicBlock getAPredecessor() { result.getASuccessor() = this }
ControlFlowElement getNode(int pos) { bbIndex(this, result, pos) }
deprecated ControlFlowElement getElement(int pos) { result = this.getNode(pos) }
ControlFlowElement getAnElement() { result = this.getNode(_) }
ControlFlowElement getFirstElement() { result = this }
ControlFlowElement getLastNode() { result = this.getNode(this.length() - 1) }
deprecated ControlFlowElement getLastElement() { result = this.getLastNode() }
int length() { result = strictcount(this.getAnElement()) }
PreBasicBlock getImmediateDominator() { bbIDominates(result, this) }
predicate immediatelyDominates(PreBasicBlock bb) { bbIDominates(this, bb) }
pragma[inline]
predicate strictlyDominates(PreBasicBlock bb) { this.immediatelyDominates+(bb) }
pragma[inline]
predicate dominates(PreBasicBlock bb) {
bb = this
or
this.strictlyDominates(bb)
}
predicate inDominanceFrontier(PreBasicBlock df) {
this = df.getAPredecessor() and not bbIDominates(this, df)
or
exists(PreBasicBlock prev | prev.inDominanceFrontier(df) |
bbIDominates(this, prev) and
not bbIDominates(this, df)
)
}
/** Unsupported. Do not use. */
predicate strictlyPostDominates(PreBasicBlock bb) { none() }
/** Unsupported. Do not use. */
predicate postDominates(PreBasicBlock bb) {
this.strictlyPostDominates(bb) or
this = bb
}
}
private Completion getConditionalCompletion(ConditionalCompletion cc) {
result.getInnerCompletion() = cc
}
pragma[nomagic]
private predicate conditionBlockImmediatelyControls(
ConditionBlock cond, PreBasicBlock succ, ConditionalCompletion cc
) {
exists(ControlFlowElement last, Completion c |
last = cond.getLastNode() and
c = getConditionalCompletion(cc) and
succ(last, succ, c) and
// In the pre-CFG, we need to account for case where one predecessor node has
// two edges to the same successor node. Assertion expressions are examples of
// such nodes.
not exists(Completion other |
succ(last, succ, other) and
other != c
) and
forall(PreBasicBlock pred | pred = succ.getAPredecessor() and pred != cond |
succ.dominates(pred)
)
)
}
class ConditionBlock extends PreBasicBlock {
ConditionBlock() {
exists(Completion c | c = getConditionalCompletion(_) |
succ(this.getLastNode(), _, c)
or
scopeLast(_, this.getLastNode(), c)
)
}
pragma[nomagic]
predicate controls(PreBasicBlock controlled, Cfg::ConditionalSuccessor s) {
exists(PreBasicBlock succ, ConditionalCompletion c |
conditionBlockImmediatelyControls(this, succ, c)
|
succ.dominates(controlled) and
s = c.getAMatchingSuccessorType()
)
}
}
module PreCfg implements BB::CfgSig<Location> {
class ControlFlowNode = ControlFlowElement;
class BasicBlock = PreBasicBlock;
class EntryBasicBlock extends BasicBlock {
EntryBasicBlock() { entryBB(this) }
}
predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) {
conditionBlockImmediatelyControls(bb1, bb2, _)
}
}

View File

@@ -1,246 +0,0 @@
import csharp
private class ControlFlowScope extends ControlFlowElement {
private boolean exactScope;
ControlFlowScope() {
exists(ControlFlowReachabilityConfiguration c |
c.candidate(_, _, this, exactScope, _) or
c.candidateDef(_, _, this, exactScope, _)
)
}
predicate isExact() { exactScope = true }
predicate isNonExact() { exactScope = false }
}
private newtype TControlFlowElementOrBasicBlock =
TControlFlowElement(ControlFlowElement cfe) or
TBasicBlock(ControlFlow::BasicBlock bb)
class ControlFlowElementOrBasicBlock extends TControlFlowElementOrBasicBlock {
ControlFlowElement asControlFlowElement() { this = TControlFlowElement(result) }
ControlFlow::BasicBlock asBasicBlock() { this = TBasicBlock(result) }
string toString() {
result = this.asControlFlowElement().toString()
or
result = this.asBasicBlock().toString()
}
Location getLocation() {
result = this.asControlFlowElement().getLocation()
or
result = this.asBasicBlock().getLocation()
}
}
private predicate isBasicBlock(ControlFlowElementOrBasicBlock c) { c instanceof TBasicBlock }
private predicate isNonExactScope(ControlFlowElementOrBasicBlock c) {
c.asControlFlowElement().(ControlFlowScope).isNonExact()
}
private predicate step(ControlFlowElementOrBasicBlock pred, ControlFlowElementOrBasicBlock succ) {
pred.asBasicBlock().getANode().getAstNode() = succ.asControlFlowElement()
or
pred.asControlFlowElement() = succ.asControlFlowElement().getAChild()
}
private predicate basicBlockInNonExactScope(
ControlFlowElementOrBasicBlock bb, ControlFlowElementOrBasicBlock scope
) = doublyBoundedFastTC(step/2, isBasicBlock/1, isNonExactScope/1)(bb, scope)
pragma[noinline]
private ControlFlow::BasicBlock getABasicBlockInScope(ControlFlowScope scope, boolean exactScope) {
basicBlockInNonExactScope(TBasicBlock(result), TControlFlowElement(scope)) and
exactScope = false
or
scope.isExact() and
result.getANode().getAstNode() = scope and
exactScope = true
}
/**
* A helper class for determining control-flow reachability for pairs of
* elements.
*
* This is useful when defining for example expression-based data-flow steps in
* the presence of control-flow splitting, where a data-flow step should make
* sure to stay in the same split.
*
* For example, in
*
* ```csharp
* if (b)
* ....
* var x = "foo";
* if (b)
* ....
* ```
*
* there should only be steps from `[b = true] "foo"` to `[b = true] SSA def(x)`
* and `[b = false] "foo"` to `[b = false] SSA def(x)`, and for example not from
* `[b = true] "foo"` to `[b = false] SSA def(x)`
*/
abstract class ControlFlowReachabilityConfiguration extends string {
bindingset[this]
ControlFlowReachabilityConfiguration() { any() }
/**
* Holds if `e1` and `e2` are expressions for which we want to find a
* control-flow path that follows control flow successors (resp.
* predecessors, as specified by `isSuccessor`) inside the syntactic scope
* `scope`. The Boolean `exactScope` indicates whether a transitive child
* of `scope` is allowed (`exactScope = false`).
*/
predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
none()
}
/**
* Holds if `e` and `def` are elements for which we want to find a
* control-flow path that follows control flow successors (resp.
* predecessors, as specified by `isSuccessor`) inside the syntactic scope
* `scope`. The Boolean `exactScope` indicates whether a transitive child
* of `scope` is allowed (`exactScope = false`).
*/
predicate candidateDef(
Expr e, AssignableDefinition def, ControlFlowElement scope, boolean exactScope,
boolean isSuccessor
) {
none()
}
pragma[nomagic]
private predicate reachesBasicBlockExprBase(
Expr e1, Expr e2, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn1, int i,
ControlFlow::BasicBlock bb
) {
this.candidate(e1, e2, _, _, isSuccessor) and
cfn1 = e1.getAControlFlowNode() and
bb.getNode(i) = cfn1
}
pragma[nomagic]
private predicate reachesBasicBlockExprRec(
Expr e1, Expr e2, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn1,
ControlFlow::BasicBlock bb
) {
exists(ControlFlow::BasicBlock mid |
this.reachesBasicBlockExpr(e1, e2, isSuccessor, cfn1, mid)
|
isSuccessor = true and
bb = mid.getASuccessor()
or
isSuccessor = false and
bb = mid.getAPredecessor()
)
}
pragma[nomagic]
private predicate reachesBasicBlockExpr(
Expr e1, Expr e2, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn1,
ControlFlow::BasicBlock bb
) {
this.reachesBasicBlockExprBase(e1, e2, isSuccessor, cfn1, _, bb)
or
exists(ControlFlowElement scope, boolean exactScope |
this.candidate(e1, e2, scope, exactScope, isSuccessor) and
this.reachesBasicBlockExprRec(e1, e2, isSuccessor, cfn1, bb) and
bb = getABasicBlockInScope(scope, exactScope)
)
}
pragma[nomagic]
private predicate reachesBasicBlockDefinitionBase(
Expr e, AssignableDefinition def, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn,
int i, ControlFlow::BasicBlock bb
) {
this.candidateDef(e, def, _, _, isSuccessor) and
cfn = e.getAControlFlowNode() and
bb.getNode(i) = cfn
}
pragma[nomagic]
private predicate reachesBasicBlockDefinitionRec(
Expr e, AssignableDefinition def, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn,
ControlFlow::BasicBlock bb
) {
exists(ControlFlow::BasicBlock mid |
this.reachesBasicBlockDefinition(e, def, isSuccessor, cfn, mid)
|
isSuccessor = true and
bb = mid.getASuccessor()
or
isSuccessor = false and
bb = mid.getAPredecessor()
)
}
pragma[nomagic]
private predicate reachesBasicBlockDefinition(
Expr e, AssignableDefinition def, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn,
ControlFlow::BasicBlock bb
) {
this.reachesBasicBlockDefinitionBase(e, def, isSuccessor, cfn, _, bb)
or
exists(ControlFlowElement scope, boolean exactScope |
this.candidateDef(e, def, scope, exactScope, isSuccessor) and
this.reachesBasicBlockDefinitionRec(e, def, isSuccessor, cfn, bb) and
bb = getABasicBlockInScope(scope, exactScope)
)
}
/**
* Holds if there is a control-flow path from `cfn1` to `cfn2`, where `cfn1` is a
* control-flow node for `e1` and `cfn2` is a control-flow node for `e2`.
*/
pragma[nomagic]
predicate hasExprPath(Expr e1, ControlFlow::Node cfn1, Expr e2, ControlFlow::Node cfn2) {
exists(ControlFlow::BasicBlock bb, boolean isSuccessor, int i, int j |
this.reachesBasicBlockExprBase(e1, e2, isSuccessor, cfn1, i, bb) and
cfn2 = bb.getNode(j) and
cfn2 = e2.getAControlFlowNode()
|
isSuccessor = true and j >= i
or
isSuccessor = false and i >= j
)
or
exists(ControlFlow::BasicBlock bb |
this.reachesBasicBlockExprRec(e1, e2, _, cfn1, bb) and
cfn2 = bb.getANode() and
cfn2 = e2.getAControlFlowNode()
)
}
/**
* Holds if there is a control-flow path from `cfn` to `cfnDef`, where `cfn` is a
* control-flow node for `e` and `cfnDef` is a control-flow node for `def`.
*/
pragma[nomagic]
predicate hasDefPath(
Expr e, ControlFlow::Node cfn, AssignableDefinition def, ControlFlow::Node cfnDef
) {
exists(ControlFlow::BasicBlock bb, boolean isSuccessor, int i, int j |
this.reachesBasicBlockDefinitionBase(e, def, isSuccessor, cfn, i, bb) and
cfnDef = bb.getNode(j) and
def.getExpr().getAControlFlowNode() = cfnDef
|
isSuccessor = true and j >= i
or
isSuccessor = false and i >= j
)
or
exists(ControlFlow::BasicBlock bb |
this.reachesBasicBlockDefinitionRec(e, def, _, cfn, bb) and
def.getExpr().getAControlFlowNode() = cfnDef and
cfnDef = bb.getANode()
)
}
}

View File

@@ -2,7 +2,6 @@ private import csharp
private import DataFlowPublic
private import DataFlowDispatch
private import DataFlowImplCommon
private import ControlFlowReachability
private import FlowSummaryImpl as FlowSummaryImpl
private import semmle.code.csharp.dataflow.FlowSummary as FlowSummary
private import semmle.code.csharp.dataflow.internal.ExternalFlow
@@ -259,34 +258,16 @@ private module ThisFlow {
}
}
/**
* Holds if there is a control-flow path from `n1` to `n2`. `n2` is either an
* expression node or an SSA definition node.
*/
pragma[nomagic]
predicate hasNodePath(ControlFlowReachabilityConfiguration conf, ExprNode n1, Node n2) {
exists(ControlFlow::Node cfn1, ControlFlow::Node cfn2 | conf.hasExprPath(_, cfn1, _, cfn2) |
cfn1 = n1.getControlFlowNode() and
cfn2 = n2.(ExprNode).getControlFlowNode()
)
or
exists(ControlFlow::Node cfn, AssignableDefinition def, ControlFlow::Node cfnDef |
conf.hasDefPath(_, cfn, def, cfnDef) and
cfn = n1.getControlFlowNode() and
n2 = TAssignableDefinitionNode(def, cfnDef)
)
}
/** Provides logic related to captured variables. */
module VariableCapture {
private import codeql.dataflow.VariableCapture as Shared
private import semmle.code.csharp.controlflow.BasicBlocks as BasicBlocks
private predicate closureFlowStep(ControlFlow::Nodes::ExprNode e1, ControlFlow::Nodes::ExprNode e2) {
e1 = LocalFlow::getALastEvalNode(e2)
e1.getExpr() = LocalFlow::getALastEvalNode(e2.getExpr())
or
exists(Ssa::Definition def, AssignableDefinition adef |
LocalFlow::defAssigns(adef, _, e1) and
LocalFlow::defAssigns(adef, _, _, e1) and
def.getAnUltimateDefinition().(Ssa::ExplicitDefinition).getADefinition() = adef and
exists(def.getAReadAtNode(e2))
)
@@ -379,7 +360,7 @@ module VariableCapture {
this = def.getExpr().getAControlFlowNode()
}
ControlFlow::Node getRhs() { LocalFlow::defAssigns(def, this, result) }
ControlFlow::Node getRhs() { LocalFlow::defAssigns(def, this, _, result) }
CapturedVariable getVariable() { result = v }
}
@@ -528,127 +509,74 @@ module SsaFlow {
/** Provides predicates related to local data flow. */
module LocalFlow {
class LocalExprStepConfiguration extends ControlFlowReachabilityConfiguration {
LocalExprStepConfiguration() { this = "LocalExprStepConfiguration" }
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
exactScope = false and
(
e1 = e2.(ParenthesizedExpr).getExpr() and
scope = e2 and
isSuccessor = true
or
e1 = e2.(NullCoalescingExpr).getAnOperand() and
scope = e2 and
isSuccessor = true
or
e1 = e2.(SuppressNullableWarningExpr).getExpr() and
scope = e2 and
isSuccessor = true
or
e2 =
any(ConditionalExpr ce |
e1 = ce.getThen() or
e1 = ce.getElse()
) and
scope = e2 and
isSuccessor = true
or
e1 = e2.(Cast).getExpr() and
scope = e2 and
isSuccessor = true
or
// An `=` expression, where the result of the expression is used
e2 =
any(AssignExpr ae |
ae.getParent() = any(ControlFlowElement cfe | not cfe instanceof ExprStmt) and
e1 = ae.getRValue()
) and
scope = e2 and
isSuccessor = true
or
e1 = e2.(ObjectCreation).getInitializer() and
scope = e2 and
isSuccessor = false
or
e1 = e2.(ArrayCreation).getInitializer() and
scope = e2 and
isSuccessor = false
or
e1 = e2.(SwitchExpr).getACase().getBody() and
scope = e2 and
isSuccessor = true
or
e1 = e2.(CheckedExpr).getExpr() and
scope = e2 and
isSuccessor = true
or
e1 = e2.(UncheckedExpr).getExpr() and
scope = e2 and
isSuccessor = true
or
e1 = e2.(CollectionExpression).getAnElement() and
e1 instanceof SpreadElementExpr and
scope = e2 and
isSuccessor = true
or
e1 = e2.(SpreadElementExpr).getExpr() and
scope = e2 and
isSuccessor = true
or
exists(WithExpr we |
scope = we and
isSuccessor = true
|
e1 = we.getExpr() and
e2 = we.getInitializer()
or
e1 = we.getInitializer() and
e2 = we
)
or
scope = any(AssignExpr ae | ae.getLValue().(TupleExpr) = e2 and ae.getRValue() = e1) and
isSuccessor = false
or
isSuccessor = true and
exists(ControlFlowElement cfe | cfe = e2.(TupleExpr).(PatternExpr).getPatternMatch() |
cfe.(IsExpr).getExpr() = e1 and scope = cfe
or
exists(Switch sw | sw.getACase() = cfe and sw.getExpr() = e1 and scope = sw)
)
predicate localExprStep(Expr e1, Expr e2) {
e1 = e2.(ParenthesizedExpr).getExpr()
or
e1 = e2.(NullCoalescingExpr).getAnOperand()
or
e1 = e2.(SuppressNullableWarningExpr).getExpr()
or
e2 =
any(ConditionalExpr ce |
e1 = ce.getThen() or
e1 = ce.getElse()
)
}
override predicate candidateDef(
Expr e, AssignableDefinition def, ControlFlowElement scope, boolean exactScope,
boolean isSuccessor
) {
// Flow from source to definition
exactScope = false and
def.getSource() = e and
(
scope = def.getExpr() and
isSuccessor = true
or
scope = def.(AssignableDefinitions::PatternDefinition).getMatch().(IsExpr) and
isSuccessor = false
or
exists(Switch s |
s.getACase() = def.(AssignableDefinitions::PatternDefinition).getMatch() and
isSuccessor = true
|
scope = s.getExpr()
or
scope = s.getACase()
)
or
e1 = e2.(Cast).getExpr()
or
// An `=` expression, where the result of the expression is used
e2 =
any(AssignExpr ae |
ae.getParent() = any(ControlFlowElement cfe | not cfe instanceof ExprStmt) and
e1 = ae.getRValue()
)
}
or
e1 = e2.(ObjectCreation).getInitializer()
or
e1 = e2.(ArrayCreation).getInitializer()
or
e1 = e2.(SwitchExpr).getACase().getBody()
or
e1 = e2.(CheckedExpr).getExpr()
or
e1 = e2.(UncheckedExpr).getExpr()
or
e1 = e2.(CollectionExpression).getAnElement() and
e1 instanceof SpreadElementExpr
or
e1 = e2.(SpreadElementExpr).getExpr()
or
exists(WithExpr we |
e1 = we.getExpr() and
e2 = we.getInitializer()
or
e1 = we.getInitializer() and
e2 = we
)
or
exists(AssignExpr ae | ae.getLValue().(TupleExpr) = e2 and ae.getRValue() = e1)
or
exists(ControlFlowElement cfe | cfe = e2.(TupleExpr).(PatternExpr).getPatternMatch() |
cfe.(IsExpr).getExpr() = e1
or
exists(Switch sw | sw.getACase() = cfe and sw.getExpr() = e1)
)
}
predicate defAssigns(AssignableDefinition def, ControlFlow::Node cfnDef, ControlFlow::Node value) {
any(LocalExprStepConfiguration x).hasDefPath(_, value, def, cfnDef)
predicate defAssigns(
AssignableDefinition def, ControlFlow::Node cfnDef, Expr value, ControlFlow::Node valueCfn
) {
def.getSource() = value and
valueCfn = value.getControlFlowNode() and
cfnDef = def.getExpr().getAControlFlowNode()
}
private predicate defAssigns(ExprNode value, AssignableDefinitionNode defNode) {
exists(ControlFlow::Node cfn, AssignableDefinition def, ControlFlow::Node cfnDef |
defAssigns(def, cfnDef, value.getExpr(), _) and
cfn = value.getControlFlowNode() and
defNode = TAssignableDefinitionNode(def, cfnDef)
)
}
/**
@@ -659,7 +587,9 @@ module LocalFlow {
}
predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) {
hasNodePath(any(LocalExprStepConfiguration x), nodeFrom, nodeTo)
localExprStep(nodeFrom.asExpr(), nodeTo.asExpr())
or
defAssigns(nodeFrom, nodeTo)
or
ThisFlow::adjacentThisRefs(nodeFrom, nodeTo) and
nodeFrom != nodeTo
@@ -685,11 +615,12 @@ module LocalFlow {
}
/**
* Gets a node that may execute last in `n`, and which, when it executes last,
* will be the value of `n`.
* Gets a node that may execute last in `e`, and which, when it executes last,
* will be the value of `e`.
*/
ControlFlow::Nodes::ExprNode getALastEvalNode(ControlFlow::Nodes::ExprNode cfn) {
exists(Expr e | any(LocalExprStepConfiguration x).hasExprPath(_, result, e, cfn) |
Expr getALastEvalNode(Expr e) {
localExprStep(result, e) and
(
e instanceof ConditionalExpr or
e instanceof Cast or
e instanceof NullCoalescingExpr or
@@ -713,9 +644,7 @@ module LocalFlow {
* we add a reverse flow step from `[post] b ? x : y` to `[post] x` and to
* `[post] y`, in order for the side-effect of `m` to reach both `x` and `y`.
*/
ControlFlow::Nodes::ExprNode getPostUpdateReverseStep(ControlFlow::Nodes::ExprNode e) {
result = getALastEvalNode(e)
}
Expr getPostUpdateReverseStep(Expr e) { result = getALastEvalNode(e) }
/**
* Holds if the value of `node2` is given by `node1`.
@@ -729,9 +658,10 @@ module LocalFlow {
e instanceof ThisAccess or e instanceof BaseAccess
)
or
hasNodePath(any(LocalExprStepConfiguration x), node1, node2) and
defAssigns(node1, node2)
or
localExprStep(node1.asExpr(), node2.asExpr()) and
(
node2 instanceof AssignableDefinitionNode or
node2.asExpr() instanceof Cast or
node2.asExpr() instanceof AssignExpr
)
@@ -775,12 +705,8 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) {
or
nodeTo = nodeFrom.(LocalFunctionCreationNode).getAnAccess(true)
or
nodeTo.(PostUpdateNode).getPreUpdateNode().(ExprNode).getControlFlowNode() =
LocalFlow::getPostUpdateReverseStep(nodeFrom
.(PostUpdateNode)
.getPreUpdateNode()
.(ExprNode)
.getControlFlowNode())
nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr() =
LocalFlow::getPostUpdateReverseStep(nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr())
) and
model = ""
or
@@ -834,11 +760,11 @@ private class Argument extends Expr {
}
/**
* Holds if `e` is an assignment of `src` to field or property `c` of `q`.
* Holds if there is an assignment of `src` to field or property `c` of `q`.
*
* `postUpdate` indicates whether the store targets a post-update node.
*/
private predicate fieldOrPropertyStore(Expr e, ContentSet c, Expr src, Expr q, boolean postUpdate) {
private predicate fieldOrPropertyStore(ContentSet c, Expr src, Expr q, boolean postUpdate) {
exists(FieldOrProperty f |
c = f.getContentSet() and
(
@@ -861,25 +787,20 @@ private predicate fieldOrPropertyStore(Expr e, ContentSet c, Expr src, Expr q, b
f = fa.getTarget() and
src = def.getSource() and
q = fa.getQualifier() and
e = def.getExpr() and
postUpdate = true
)
or
// `with` expression initializer, `x with { f = src }`
e =
any(WithExpr we |
exists(MemberInitializer mi |
q = we and
mi = we.getInitializer().getAMemberInitializer() and
f = mi.getInitializedMember() and
src = mi.getRValue() and
postUpdate = false
)
)
exists(WithExpr we, MemberInitializer mi |
q = we and
mi = we.getInitializer().getAMemberInitializer() and
f = mi.getInitializedMember() and
src = mi.getRValue() and
postUpdate = false
)
or
// Object initializer, `new C() { f = src }`
exists(MemberInitializer mi |
e = q and
mi = q.(ObjectInitializer).getAMemberInitializer() and
q.getParent() instanceof ObjectCreation and
f = mi.getInitializedMember() and
@@ -888,16 +809,13 @@ private predicate fieldOrPropertyStore(Expr e, ContentSet c, Expr src, Expr q, b
)
or
// Tuple element, `(..., src, ...)` `f` is `ItemX` of tuple `q`
e =
any(TupleExpr te |
exists(int i |
e = q and
src = te.getArgument(i) and
te.isConstruction() and
f = q.getType().(TupleType).getElement(i) and
postUpdate = false
)
)
exists(TupleExpr te, int i |
te = q and
src = te.getArgument(i) and
te.isConstruction() and
f = q.getType().(TupleType).getElement(i) and
postUpdate = false
)
)
or
// A write to a dynamic property
@@ -907,7 +825,6 @@ private predicate fieldOrPropertyStore(Expr e, ContentSet c, Expr src, Expr q, b
c.isDynamicProperty(dp) and
src = def.getSource() and
q = dma.getQualifier() and
e = def.getExpr() and
postUpdate = true
)
}
@@ -943,22 +860,20 @@ private predicate collectionStore(Expr src, CollectionExpression ce) {
}
/**
* Holds if `e` is an expression that adds `src` to array `a`.
* Holds if there is an expression that adds `src` to array `a`.
*
* `postUpdate` indicates whether the store targets a post-update node.
*/
private predicate arrayStore(Expr e, Expr src, Expr a, boolean postUpdate) {
private predicate arrayStore(Expr src, Expr a, boolean postUpdate) {
// Direct assignment, `a[i] = src`
exists(AssignableDefinition def |
a = def.getTargetAccess().(ArrayWrite).getQualifier() and
src = def.getSource() and
e = def.getExpr() and
postUpdate = true
)
or
// Array initializer, `new [] { src }`
src = a.(ArrayInitializer).getAnElement() and
e = a and
postUpdate = false
or
// Member initializer, `new C { Array = { [i] = src } }`
@@ -966,7 +881,6 @@ private predicate arrayStore(Expr e, Expr src, Expr a, boolean postUpdate) {
mi = a.(ObjectInitializer).getAMemberInitializer() and
mi.getLValue() instanceof ArrayAccess and
mi.getRValue() = src and
e = a and
postUpdate = false
)
}
@@ -1141,17 +1055,17 @@ private module Cached {
(
cfn.getExpr() instanceof Argument
or
cfn =
LocalFlow::getPostUpdateReverseStep(any(ControlFlow::Nodes::ExprNode e |
exists(any(SourcePostUpdateNode p).getPreUpdateNode().asExprAtNode(e))
))
cfn.getExpr() =
LocalFlow::getPostUpdateReverseStep(any(SourcePostUpdateNode p)
.getPreUpdateNode()
.asExpr())
) and
exprMayHavePostUpdateNode(cfn.getExpr())
or
exists(Expr e | e = cfn.getExpr() |
fieldOrPropertyStore(_, _, _, e, true)
fieldOrPropertyStore(_, _, e, true)
or
arrayStore(_, _, e, true)
arrayStore(_, e, true)
or
// needed for reverse stores; e.g. `x.f1.f2 = y` induces
// a store step of `f1` into `x`
@@ -1166,7 +1080,7 @@ private module Cached {
)
)
or
lambdaCallExpr(_, cfn)
lambdaCallExpr(_, _, cfn)
} or
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) {
sn.getSummarizedCallable() instanceof CallableUsedInSource
@@ -1563,35 +1477,15 @@ abstract private class ArgumentNodeImpl extends Node {
}
private module ArgumentNodes {
private class ArgumentConfiguration extends ControlFlowReachabilityConfiguration {
ArgumentConfiguration() { this = "ArgumentConfiguration" }
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
e1.(Argument).isArgumentOf(e2, _) and
exactScope = false and
isSuccessor = true and
if e2 instanceof PropertyWrite
then
exists(AssignableDefinition def |
def.getTargetAccess() = e2 and
scope = def.getExpr()
)
else scope = e2
}
}
/** A data-flow node that represents an explicit call argument. */
class ExplicitArgumentNode extends ArgumentNodeImpl {
ExplicitArgumentNode() { this.asExpr() instanceof Argument }
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
exists(ArgumentConfiguration x, Expr c, Argument arg |
exists(Expr c, Argument arg |
arg = this.asExpr() and
c = call.getExpr() and
arg.isArgumentOf(c, pos) and
x.hasExprPath(_, this.getControlFlowNode(), _, call.getControlFlowNode())
arg.isArgumentOf(c, pos)
)
}
}
@@ -1600,7 +1494,7 @@ private module ArgumentNodes {
class DelegateSelfArgumentNode extends ArgumentNodeImpl, ExprNode {
private DataFlowCall call_;
DelegateSelfArgumentNode() { lambdaCallExpr(call_, this.getControlFlowNode()) }
DelegateSelfArgumentNode() { lambdaCallExpr(call_, this.getExpr(), _) }
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
call = call_ and
@@ -1857,27 +1751,6 @@ private module OutNodes {
}
}
class ObjectOrCollectionInitializerConfiguration extends ControlFlowReachabilityConfiguration {
ObjectOrCollectionInitializerConfiguration() {
this = "ObjectOrCollectionInitializerConfiguration"
}
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
exactScope = false and
scope = e1 and
isSuccessor = true and
exists(ObjectOrCollectionInitializer init | init = e1.(ObjectCreation).getInitializer() |
// E.g. `new Dictionary<int, string>{ {0, "a"}, {1, "b"} }`
e2 = init.(CollectionInitializer).getAnElementInitializer()
or
// E.g. `new Dictionary<int, string>() { [0] = "a", [1] = "b" }`
e2 = init.(ObjectInitializer).getAMemberInitializer().getLValue()
)
}
}
/**
* A data-flow node that reads a value returned by a callable using an
* `out` or `ref` parameter.
@@ -2236,30 +2109,6 @@ predicate jumpStep(Node pred, Node succ) {
succ = pred.(LocalFunctionCreationNode).getAnAccess(false)
}
private class StoreStepConfiguration extends ControlFlowReachabilityConfiguration {
StoreStepConfiguration() { this = "StoreStepConfiguration" }
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
exactScope = false and
fieldOrPropertyStore(scope, _, e1, e2, isSuccessor.booleanNot())
or
exactScope = false and
arrayStore(scope, e1, e2, isSuccessor.booleanNot())
or
exactScope = false and
isSuccessor = true and
collectionStore(e1, e2) and
scope = e2
or
exactScope = false and
isSuccessor = true and
isParamsArg(e2, e1, _) and
scope = e2
}
}
pragma[nomagic]
private ContentSet getResultContent() {
result.isProperty(any(SystemThreadingTasksTaskTClass c_).getResultProperty())
@@ -2282,21 +2131,17 @@ private predicate recordParameter(RecordType t, Parameter p, string name) {
}
private predicate storeContentStep(Node node1, Content c, Node node2) {
exists(StoreStepConfiguration x, ExprNode node, boolean postUpdate |
hasNodePath(x, node1, node) and
exists(ExprNode node, boolean postUpdate |
if postUpdate = true then node = node2.(PostUpdateNode).getPreUpdateNode() else node = node2
|
arrayStore(_, node1.asExpr(), node.getExpr(), postUpdate) and c instanceof ElementContent
arrayStore(node1.asExpr(), node.getExpr(), postUpdate) and c instanceof ElementContent
)
or
exists(StoreStepConfiguration x | hasNodePath(x, node1, node2) |
collectionStore(node1.asExpr(), node2.asExpr()) and c instanceof ElementContent
)
collectionStore(node1.asExpr(), node2.asExpr()) and c instanceof ElementContent
or
exists(StoreStepConfiguration x, Expr arg, ControlFlow::Node callCfn |
x.hasExprPath(arg, node1.(ExprNode).getControlFlowNode(), _, callCfn) and
node2 = TParamsArgumentNode(callCfn) and
isParamsArg(_, arg, _) and
exists(Call call |
node2 = TParamsArgumentNode(call.getControlFlowNode()) and
isParamsArg(call, node1.asExpr(), _) and
c instanceof ElementContent
)
or
@@ -2352,11 +2197,10 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
c.isSingleton(cont)
)
or
exists(StoreStepConfiguration x, ExprNode node, boolean postUpdate |
hasNodePath(x, node1, node) and
exists(ExprNode node, boolean postUpdate |
if postUpdate = true then node = node2.(PostUpdateNode).getPreUpdateNode() else node = node2
|
fieldOrPropertyStore(_, c, node1.asExpr(), node.getExpr(), postUpdate)
fieldOrPropertyStore(c, node1.asExpr(), node.getExpr(), postUpdate)
)
or
exists(Expr e |
@@ -2378,133 +2222,51 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
storeStepDelegateCall(node1, c, node2)
}
pragma[nomagic]
private predicate isAssignExprLValueDescendant(Expr e) {
e = any(AssignExpr ae).getLValue()
or
exists(Expr parent |
isAssignExprLValueDescendant(parent) and
e = parent.getAChildExpr()
)
}
private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration {
ReadStepConfiguration() { this = "ReadStepConfiguration" }
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
exactScope = false and
isSuccessor = true and
fieldOrPropertyRead(e1, _, e2) and
scope = e2
or
exactScope = false and
isSuccessor = true and
dynamicPropertyRead(e1, _, e2) and
scope = e2
or
exactScope = false and
isSuccessor = true and
arrayRead(e1, e2) and
scope = e2
or
exactScope = false and
e1 = e2.(AwaitExpr).getExpr() and
scope = e2 and
isSuccessor = true
or
exactScope = false and
e2 = e1.(TupleExpr).getAnArgument() and
scope = e1 and
isSuccessor = false
}
override predicate candidateDef(
Expr e, AssignableDefinition defTo, ControlFlowElement scope, boolean exactScope,
boolean isSuccessor
) {
exists(ForeachStmt fs |
e = fs.getIterableExpr() and
defTo.(AssignableDefinitions::LocalVariableDefinition).getDeclaration() =
fs.getVariableDeclExpr() and
isSuccessor = true
|
scope = fs and
exactScope = true
or
scope = fs.getIterableExpr() and
exactScope = false
or
scope = fs.getVariableDeclExpr() and
exactScope = false
)
or
scope =
any(AssignExpr ae |
ae = defTo.(AssignableDefinitions::TupleAssignmentDefinition).getAssignment() and
isAssignExprLValueDescendant(e.(TupleExpr)) and
exactScope = false and
isSuccessor = true
)
or
scope =
any(TupleExpr te |
te.getAnArgument() = defTo.(AssignableDefinitions::LocalVariableDefinition).getDeclaration() and
e = te and
exactScope = false and
isSuccessor = false
)
}
}
private predicate readContentStep(Node node1, Content c, Node node2) {
exists(ReadStepConfiguration x |
hasNodePath(x, node1, node2) and
arrayRead(node1.asExpr(), node2.asExpr()) and
arrayRead(node1.asExpr(), node2.asExpr()) and
c instanceof ElementContent
or
exists(
ForeachStmt fs, Ssa::ExplicitDefinition def,
AssignableDefinitions::LocalVariableDefinition defTo
|
node1.asExpr() = fs.getIterableExpr() and
defTo.getDeclaration() = fs.getVariableDeclExpr() and
def.getADefinition() = defTo and
node2.(SsaDefinitionNode).getDefinition() = def and
c instanceof ElementContent
)
or
node1 =
any(InstanceParameterAccessPreNode n |
n.getUnderlyingControlFlowNode() = node2.(ExprNode).getControlFlowNode() and
n.getParameter() = c.(PrimaryConstructorParameterContent).getParameter()
) and
node2.asExpr() instanceof ParameterRead
or
// node1 = (..., node2, ...)
// node1.ItemX flows to node2
exists(TupleExpr te, int i, Expr item |
te = node1.asExpr() and
not te.isConstruction() and
c.(FieldContent).getField() = te.getType().(TupleType).getElement(i).getUnboundDeclaration() and
// node1 = (..., item, ...)
te.getArgument(i) = item
|
// item = (..., ..., ...) in node1 = (..., (..., ..., ...), ...)
node2.asExpr().(TupleExpr) = item
or
exists(ForeachStmt fs, Ssa::ExplicitDefinition def |
x.hasDefPath(fs.getIterableExpr(), node1.getControlFlowNode(), def.getADefinition(),
def.getControlFlowNode()) and
node2.(SsaDefinitionNode).getDefinition() = def and
c instanceof ElementContent
// item = variable in node1 = (..., variable, ...)
exists(AssignableDefinitions::TupleAssignmentDefinition tad |
node2.(AssignableDefinitionNode).getDefinition() = tad and
tad.getLeaf() = item
)
or
node1 =
any(InstanceParameterAccessPreNode n |
n.getUnderlyingControlFlowNode() = node2.(ExprNode).getControlFlowNode() and
n.getParameter() = c.(PrimaryConstructorParameterContent).getParameter()
) and
node2.asExpr() instanceof ParameterRead
or
// node1 = (..., node2, ...)
// node1.ItemX flows to node2
exists(TupleExpr te, int i, Expr item |
te = node1.asExpr() and
not te.isConstruction() and
c.(FieldContent).getField() = te.getType().(TupleType).getElement(i).getUnboundDeclaration() and
// node1 = (..., item, ...)
te.getArgument(i) = item
|
// item = (..., ..., ...) in node1 = (..., (..., ..., ...), ...)
node2.asExpr().(TupleExpr) = item and
hasNodePath(x, node1, node2)
or
// item = variable in node1 = (..., variable, ...)
exists(AssignableDefinitions::TupleAssignmentDefinition tad |
node2.(AssignableDefinitionNode).getDefinition() = tad and
tad.getLeaf() = item and
hasNodePath(x, node1, node2)
)
or
// item = variable in node1 = (..., variable, ...) in a case/is var (..., ...)
isPatternExprDescendant(te) and
exists(AssignableDefinitions::LocalVariableDefinition lvd |
node2.(AssignableDefinitionNode).getDefinition() = lvd and
lvd.getDeclaration() = item and
hasNodePath(x, node1, node2)
)
// item = variable in node1 = (..., variable, ...) in a case/is var (..., ...)
isPatternExprDescendant(te) and
exists(AssignableDefinitions::LocalVariableDefinition lvd |
node2.(AssignableDefinitionNode).getDefinition() = lvd and
lvd.getDeclaration() = item
)
)
or
@@ -2535,14 +2297,12 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
c.isSingleton(cont)
)
or
exists(ReadStepConfiguration x | hasNodePath(x, node1, node2) |
fieldOrPropertyRead(node1.asExpr(), c, node2.asExpr())
or
dynamicPropertyRead(node1.asExpr(), c, node2.asExpr())
or
node2.asExpr().(AwaitExpr).getExpr() = node1.asExpr() and
c = getResultContent()
)
fieldOrPropertyRead(node1.asExpr(), c, node2.asExpr())
or
dynamicPropertyRead(node1.asExpr(), c, node2.asExpr())
or
node2.asExpr().(AwaitExpr).getExpr() = node1.asExpr() and
c = getResultContent()
or
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
node2.(FlowSummaryNode).getSummaryNode())
@@ -2576,9 +2336,9 @@ predicate clearsContent(Node n, ContentSet c) {
c.isSingleton(cont)
)
or
fieldOrPropertyStore(_, c, _, n.asExpr(), true)
fieldOrPropertyStore(c, _, n.asExpr(), true)
or
fieldOrPropertyStore(_, c, _, n.(ObjectInitializerNode).getInitializer(), false)
fieldOrPropertyStore(c, _, n.(ObjectInitializerNode).getInitializer(), false)
or
FlowSummaryImpl::Private::Steps::summaryClearsContent(n.(FlowSummaryNode).getSummaryNode(), c)
or
@@ -2817,8 +2577,13 @@ module PostUpdateNodes {
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
pos.isQualifier() and
any(ObjectOrCollectionInitializerConfiguration x)
.hasExprPath(_, cfn, _, call.getControlFlowNode())
exists(ObjectOrCollectionInitializer init | init = oc.getInitializer() |
// E.g. `new Dictionary<int, string>{ {0, "a"}, {1, "b"} }`
call.getExpr() = init.(CollectionInitializer).getAnElementInitializer()
or
// E.g. `new Dictionary<int, string>() { [0] = "a", [1] = "b" }`
call.getExpr() = init.(ObjectInitializer).getAMemberInitializer().getLValue()
)
}
override DataFlowCallable getEnclosingCallableImpl() {
@@ -2980,45 +2745,26 @@ private predicate isLocalFunctionCallReceiver(
f = receiver.getTarget().getUnboundDeclaration()
}
private class LambdaConfiguration extends ControlFlowReachabilityConfiguration {
LambdaConfiguration() { this = "LambdaConfiguration" }
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
e1 = e2.(DelegateLikeCall).getExpr() and
exactScope = false and
scope = e2 and
isSuccessor = true
or
e1 = e2.(DelegateCreation).getArgument() and
exactScope = false and
scope = e2 and
isSuccessor = true
or
isLocalFunctionCallReceiver(e2, e1, _) and
exactScope = false and
scope = e2 and
isSuccessor = true
}
}
private predicate lambdaCallExpr(DataFlowCall call, ControlFlow::Node receiver) {
exists(LambdaConfiguration x, DelegateLikeCall dc |
x.hasExprPath(dc.getExpr(), receiver, dc, call.getControlFlowNode())
private predicate lambdaCallExpr(DataFlowCall call, Expr receiver, ControlFlow::Node receiverCfn) {
exists(DelegateLikeCall dc |
call.(ExplicitDelegateLikeDataFlowCall).getCall() = dc and
receiver = dc.getExpr() and
receiverCfn = receiver.getControlFlowNode()
)
or
// In local function calls, `F()`, we use the local function access `F`
// to represent the receiver. Only needed for flow through captured variables.
exists(LambdaConfiguration x, LocalFunctionCall fc |
x.hasExprPath(fc.getAChild(), receiver, fc, call.getControlFlowNode())
exists(LocalFunctionCall fc |
receiver = fc.getAChild() and
receiverCfn = receiver.getControlFlowNode() and
fc.getControlFlowNode() = call.getControlFlowNode()
)
}
/** Holds if `call` is a lambda call where `receiver` is the lambda expression. */
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
(
lambdaCallExpr(call, receiver.(ExprNode).getControlFlowNode()) and
lambdaCallExpr(call, receiver.asExpr(), _) and
// local function calls can be resolved directly without a flow analysis
not call.getControlFlowNode().getAstNode() instanceof LocalFunctionCall
or
@@ -3028,9 +2774,9 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
}
private predicate delegateCreationStep(Node nodeFrom, Node nodeTo) {
exists(LambdaConfiguration x, DelegateCreation dc |
x.hasExprPath(dc.getArgument(), nodeFrom.(ExprNode).getControlFlowNode(), dc,
nodeTo.(ExprNode).getControlFlowNode())
exists(DelegateCreation dc |
dc.getArgument() = nodeFrom.asExpr() and
dc = nodeTo.asExpr()
)
}

View File

@@ -239,7 +239,7 @@ module ModelValidation {
)
}
string getIncorrectConstructorSummaryOutput() {
private string getIncorrectConstructorSummaryOutput() {
exists(string namespace, string type, string name, string output |
type = name or
type = name + "<" + any(string s)

View File

@@ -4,7 +4,6 @@ private import FlowSummaryImpl as FlowSummaryImpl
private import semmle.code.csharp.Caching
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
private import semmle.code.csharp.dataflow.internal.ControlFlowReachability
private import semmle.code.csharp.dispatch.Dispatch
private import semmle.code.csharp.commons.ComparisonTest
// import `TaintedMember` definitions from other files to avoid potential reevaluation
@@ -45,82 +44,58 @@ predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::ContentSet c)
)
}
private class LocalTaintExprStepConfiguration extends ControlFlowReachabilityConfiguration {
LocalTaintExprStepConfiguration() { this = "LocalTaintExprStepConfiguration" }
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
exactScope = false and
isSuccessor = true and
(
e1 = e2.(ElementAccess).getQualifier() and
scope = e2
or
e1 = e2.(AddExpr).getAnOperand() and
scope = e2
or
// A comparison expression where taint can flow from one of the
// operands if the other operand is a constant value.
exists(ComparisonTest ct, Expr other |
ct.getExpr() = e2 and
e1 = ct.getAnArgument() and
other = ct.getAnArgument() and
other.stripCasts().hasValue() and
e1 != other and
scope = e2
)
or
e1 = e2.(UnaryLogicalOperation).getAnOperand() and
scope = e2
or
e1 = e2.(BinaryLogicalOperation).getAnOperand() and
scope = e2
or
e1 = e2.(InterpolatedStringExpr).getAChild() and
scope = e2
or
e1 = e2.(InterpolatedStringInsertExpr).getInsert() and
scope = e2
or
e2 =
any(OperatorCall oc |
oc.getTarget().(ConversionOperator).fromLibrary() and
e1 = oc.getAnArgument() and
scope = e2
)
or
e1 = e2.(AwaitExpr).getExpr() and
scope = e2
or
// Taint flows from the operand of a cast to the cast expression if the cast is to an interpolated string handler.
e2 =
any(CastExpr ce |
e1 = ce.getExpr() and
scope = ce and
ce.getTargetType()
.(Attributable)
.getAnAttribute()
.getType()
.hasFullyQualifiedName("System.Runtime.CompilerServices",
"InterpolatedStringHandlerAttribute")
)
)
}
}
private ControlFlow::Nodes::ExprNode getALastEvalNode(ControlFlow::Nodes::ExprNode cfn) {
exists(OperatorCall oc | any(LocalTaintExprStepConfiguration x).hasExprPath(_, result, oc, cfn) |
oc.getTarget() instanceof ImplicitConversionOperator
private predicate localTaintExprStep(Expr e1, Expr e2) {
e1 = e2.(ElementAccess).getQualifier()
or
e1 = e2.(AddExpr).getAnOperand()
or
// A comparison expression where taint can flow from one of the
// operands if the other operand is a constant value.
exists(ComparisonTest ct, Expr other |
ct.getExpr() = e2 and
e1 = ct.getAnArgument() and
other = ct.getAnArgument() and
other.stripCasts().hasValue() and
e1 != other
)
or
e1 = e2.(UnaryLogicalOperation).getAnOperand()
or
e1 = e2.(BinaryLogicalOperation).getAnOperand()
or
e1 = e2.(InterpolatedStringExpr).getAChild()
or
e1 = e2.(InterpolatedStringInsertExpr).getInsert()
or
e2 =
any(OperatorCall oc |
oc.getTarget().(ConversionOperator).fromLibrary() and
e1 = oc.getAnArgument()
)
or
e1 = e2.(AwaitExpr).getExpr()
or
// Taint flows from the operand of a cast to the cast expression if the cast is to an interpolated string handler.
e2 =
any(CastExpr ce |
e1 = ce.getExpr() and
ce.getTargetType()
.(Attributable)
.getAnAttribute()
.getType()
.hasFullyQualifiedName("System.Runtime.CompilerServices",
"InterpolatedStringHandlerAttribute")
)
}
private ControlFlow::Nodes::ExprNode getPostUpdateReverseStep(ControlFlow::Nodes::ExprNode e) {
result = getALastEvalNode(e)
private Expr getALastEvalNode(OperatorCall oc) {
localTaintExprStep(result, oc) and oc.getTarget() instanceof ImplicitConversionOperator
}
private Expr getPostUpdateReverseStep(Expr e) { result = getALastEvalNode(e) }
private predicate localTaintStepCommon(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
hasNodePath(any(LocalTaintExprStepConfiguration x), nodeFrom, nodeTo)
localTaintExprStep(nodeFrom.asExpr(), nodeTo.asExpr())
}
cached
@@ -191,12 +166,8 @@ private module Cached {
// Allow reverse update flow for implicit conversion operator calls.
// This is needed to support flow out of method call arguments, where an implicit conversion is applied
// to a call argument.
nodeTo.(PostUpdateNode).getPreUpdateNode().(DataFlow::ExprNode).getControlFlowNode() =
getPostUpdateReverseStep(nodeFrom
.(PostUpdateNode)
.getPreUpdateNode()
.(DataFlow::ExprNode)
.getControlFlowNode())
nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr() =
getPostUpdateReverseStep(nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr())
) and
model = ""
or

View File

@@ -1,246 +0,0 @@
import csharp
private class ControlFlowScope extends ControlFlowElement {
private boolean exactScope;
ControlFlowScope() {
exists(ControlFlowReachabilityConfiguration c |
c.candidate(_, _, this, exactScope, _) or
c.candidateDef(_, _, this, exactScope, _)
)
}
predicate isExact() { exactScope = true }
predicate isNonExact() { exactScope = false }
}
private newtype TControlFlowElementOrBasicBlock =
TControlFlowElement(ControlFlowElement cfe) or
TBasicBlock(ControlFlow::BasicBlock bb)
class ControlFlowElementOrBasicBlock extends TControlFlowElementOrBasicBlock {
ControlFlowElement asControlFlowElement() { this = TControlFlowElement(result) }
ControlFlow::BasicBlock asBasicBlock() { this = TBasicBlock(result) }
string toString() {
result = this.asControlFlowElement().toString()
or
result = this.asBasicBlock().toString()
}
Location getLocation() {
result = this.asControlFlowElement().getLocation()
or
result = this.asBasicBlock().getLocation()
}
}
private predicate isBasicBlock(ControlFlowElementOrBasicBlock c) { c instanceof TBasicBlock }
private predicate isNonExactScope(ControlFlowElementOrBasicBlock c) {
c.asControlFlowElement().(ControlFlowScope).isNonExact()
}
private predicate step(ControlFlowElementOrBasicBlock pred, ControlFlowElementOrBasicBlock succ) {
pred.asBasicBlock().getANode().getAstNode() = succ.asControlFlowElement()
or
pred.asControlFlowElement() = succ.asControlFlowElement().getAChild()
}
private predicate basicBlockInNonExactScope(
ControlFlowElementOrBasicBlock bb, ControlFlowElementOrBasicBlock scope
) = doublyBoundedFastTC(step/2, isBasicBlock/1, isNonExactScope/1)(bb, scope)
pragma[noinline]
private ControlFlow::BasicBlock getABasicBlockInScope(ControlFlowScope scope, boolean exactScope) {
basicBlockInNonExactScope(TBasicBlock(result), TControlFlowElement(scope)) and
exactScope = false
or
scope.isExact() and
result.getANode().getAstNode() = scope and
exactScope = true
}
/**
* A helper class for determining control-flow reachability for pairs of
* elements.
*
* This is useful when defining for example expression-based data-flow steps in
* the presence of control-flow splitting, where a data-flow step should make
* sure to stay in the same split.
*
* For example, in
*
* ```csharp
* if (b)
* ....
* var x = "foo";
* if (b)
* ....
* ```
*
* there should only be steps from `[b = true] "foo"` to `[b = true] SSA def(x)`
* and `[b = false] "foo"` to `[b = false] SSA def(x)`, and for example not from
* `[b = true] "foo"` to `[b = false] SSA def(x)`
*/
abstract class ControlFlowReachabilityConfiguration extends string {
bindingset[this]
ControlFlowReachabilityConfiguration() { any() }
/**
* Holds if `e1` and `e2` are expressions for which we want to find a
* control-flow path that follows control flow successors (resp.
* predecessors, as specified by `isSuccessor`) inside the syntactic scope
* `scope`. The Boolean `exactScope` indicates whether a transitive child
* of `scope` is allowed (`exactScope = false`).
*/
predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
none()
}
/**
* Holds if `e` and `def` are elements for which we want to find a
* control-flow path that follows control flow successors (resp.
* predecessors, as specified by `isSuccessor`) inside the syntactic scope
* `scope`. The Boolean `exactScope` indicates whether a transitive child
* of `scope` is allowed (`exactScope = false`).
*/
predicate candidateDef(
Expr e, AssignableDefinition def, ControlFlowElement scope, boolean exactScope,
boolean isSuccessor
) {
none()
}
pragma[nomagic]
private predicate reachesBasicBlockExprBase(
Expr e1, Expr e2, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn1, int i,
ControlFlow::BasicBlock bb
) {
this.candidate(e1, e2, _, _, isSuccessor) and
cfn1 = e1.getAControlFlowNode() and
bb.getNode(i) = cfn1
}
pragma[nomagic]
private predicate reachesBasicBlockExprRec(
Expr e1, Expr e2, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn1,
ControlFlow::BasicBlock bb
) {
exists(ControlFlow::BasicBlock mid |
this.reachesBasicBlockExpr(e1, e2, isSuccessor, cfn1, mid)
|
isSuccessor = true and
bb = mid.getASuccessor()
or
isSuccessor = false and
bb = mid.getAPredecessor()
)
}
pragma[nomagic]
private predicate reachesBasicBlockExpr(
Expr e1, Expr e2, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn1,
ControlFlow::BasicBlock bb
) {
this.reachesBasicBlockExprBase(e1, e2, isSuccessor, cfn1, _, bb)
or
exists(ControlFlowElement scope, boolean exactScope |
this.candidate(e1, e2, scope, exactScope, isSuccessor) and
this.reachesBasicBlockExprRec(e1, e2, isSuccessor, cfn1, bb) and
bb = getABasicBlockInScope(scope, exactScope)
)
}
pragma[nomagic]
private predicate reachesBasicBlockDefinitionBase(
Expr e, AssignableDefinition def, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn,
int i, ControlFlow::BasicBlock bb
) {
this.candidateDef(e, def, _, _, isSuccessor) and
cfn = e.getAControlFlowNode() and
bb.getNode(i) = cfn
}
pragma[nomagic]
private predicate reachesBasicBlockDefinitionRec(
Expr e, AssignableDefinition def, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn,
ControlFlow::BasicBlock bb
) {
exists(ControlFlow::BasicBlock mid |
this.reachesBasicBlockDefinition(e, def, isSuccessor, cfn, mid)
|
isSuccessor = true and
bb = mid.getASuccessor()
or
isSuccessor = false and
bb = mid.getAPredecessor()
)
}
pragma[nomagic]
private predicate reachesBasicBlockDefinition(
Expr e, AssignableDefinition def, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn,
ControlFlow::BasicBlock bb
) {
this.reachesBasicBlockDefinitionBase(e, def, isSuccessor, cfn, _, bb)
or
exists(ControlFlowElement scope, boolean exactScope |
this.candidateDef(e, def, scope, exactScope, isSuccessor) and
this.reachesBasicBlockDefinitionRec(e, def, isSuccessor, cfn, bb) and
bb = getABasicBlockInScope(scope, exactScope)
)
}
/**
* Holds if there is a control-flow path from `cfn1` to `cfn2`, where `cfn1` is a
* control-flow node for `e1` and `cfn2` is a control-flow node for `e2`.
*/
pragma[nomagic]
predicate hasExprPath(Expr e1, ControlFlow::Node cfn1, Expr e2, ControlFlow::Node cfn2) {
exists(ControlFlow::BasicBlock bb, boolean isSuccessor, int i, int j |
this.reachesBasicBlockExprBase(e1, e2, isSuccessor, cfn1, i, bb) and
cfn2 = bb.getNode(j) and
cfn2 = e2.getAControlFlowNode()
|
isSuccessor = true and j >= i
or
isSuccessor = false and i >= j
)
or
exists(ControlFlow::BasicBlock bb |
this.reachesBasicBlockExprRec(e1, e2, _, cfn1, bb) and
cfn2 = bb.getANode() and
cfn2 = e2.getAControlFlowNode()
)
}
/**
* Holds if there is a control-flow path from `cfn` to `cfnDef`, where `cfn` is a
* control-flow node for `e` and `cfnDef` is a control-flow node for `def`.
*/
pragma[nomagic]
predicate hasDefPath(
Expr e, ControlFlow::Node cfn, AssignableDefinition def, ControlFlow::Node cfnDef
) {
exists(ControlFlow::BasicBlock bb, boolean isSuccessor, int i, int j |
this.reachesBasicBlockDefinitionBase(e, def, isSuccessor, cfn, i, bb) and
cfnDef = bb.getNode(j) and
def.getExpr().getAControlFlowNode() = cfnDef
|
isSuccessor = true and j >= i
or
isSuccessor = false and i >= j
)
or
exists(ControlFlow::BasicBlock bb |
this.reachesBasicBlockDefinitionRec(e, def, _, cfn, bb) and
def.getExpr().getAControlFlowNode() = cfnDef and
cfnDef = bb.getANode()
)
}
}

View File

@@ -8,26 +8,14 @@ private module Impl {
private import ConstantUtils
private import SsaReadPositionCommon
private import semmle.code.csharp.controlflow.Guards as G
private import ControlFlowReachability
private class ExprNode = ControlFlow::Nodes::ExprNode;
private class ExprChildReachability extends ControlFlowReachabilityConfiguration {
ExprChildReachability() { this = "ExprChildReachability" }
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
e2 = e1.getAChild() and
scope = e1 and
exactScope = false and
isSuccessor in [false, true]
}
}
/** Holds if `parent` having child `child` implies `parentNode` having child `childNode`. */
predicate hasChild(Expr parent, Expr child, ExprNode parentNode, ExprNode childNode) {
any(ExprChildReachability x).hasExprPath(parent, parentNode, child, childNode)
parent.getAChild() = child and
parentNode = parent.getControlFlowNode() and
childNode = child.getControlFlowNode()
}
/** Holds if SSA definition `def` equals `e + delta`. */

View File

@@ -52,9 +52,17 @@ private class HtmlSanitizer extends Sanitizer {
}
/**
* An argument to a call to a method on a logger class.
* An argument to a call to a method on a logger class, excluding extension methods
* with source code which are analyzed interprocedurally.
*/
private class LogForgingLogMessageSink extends Sink, LogMessageSink { }
private class LogForgingLogMessageSink extends Sink, LogMessageSink {
LogForgingLogMessageSink() {
not exists(ExtensionMethodCall mc |
this.getExpr() = mc.getAnArgument() and
mc.getTarget().fromSource()
)
}
}
/**
* An argument to a call to a method on a trace class.

View File

@@ -1,3 +1,7 @@
## 1.6.4
No user-facing changes.
## 1.6.3
No user-facing changes.

View File

@@ -4,7 +4,7 @@
* allows for a cross-site scripting vulnerability.
* @kind path-problem
* @problem.severity error
* @security-severity 6.1
* @security-severity 7.8
* @precision high
* @id cs/web/xss
* @tags security

View File

@@ -4,7 +4,7 @@
* insertion of forged log entries by a malicious user.
* @kind path-problem
* @problem.severity error
* @security-severity 7.8
* @security-severity 6.1
* @precision high
* @id cs/log-forging
* @tags security

View File

@@ -0,0 +1,5 @@
---
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).

View File

@@ -0,0 +1,3 @@
## 1.6.4
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.6.3
lastReleaseVersion: 1.6.4

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-queries
version: 1.6.4-dev
version: 1.6.5-dev
groups:
- csharp
- queries

View File

@@ -26,6 +26,7 @@
| CSharp7.cs:31:16:31:16 | access to parameter i | CSharp7.cs:31:16:31:20 | ... > ... |
| CSharp7.cs:31:16:31:16 | access to parameter i | CSharp7.cs:31:24:31:24 | access to parameter i |
| CSharp7.cs:31:24:31:24 | access to parameter i | CSharp7.cs:31:16:31:59 | ... ? ... : ... |
| CSharp7.cs:31:28:31:59 | throw ... | CSharp7.cs:31:16:31:59 | ... ? ... : ... |
| CSharp7.cs:35:7:35:18 | this | CSharp7.cs:35:7:35:18 | this access |
| CSharp7.cs:39:9:39:9 | access to parameter x | CSharp7.cs:39:9:39:21 | SSA def(x) |
| CSharp7.cs:39:13:39:21 | "tainted" | CSharp7.cs:39:9:39:9 | access to parameter x |
@@ -253,6 +254,7 @@
| CSharp7.cs:233:13:233:13 | access to local variable o | CSharp7.cs:235:13:235:42 | [input] SSA phi read(o) |
| CSharp7.cs:233:13:233:13 | access to local variable o | CSharp7.cs:237:18:237:18 | access to local variable o |
| CSharp7.cs:233:13:233:23 | [false] ... is ... | CSharp7.cs:233:13:233:33 | [false] ... && ... |
| CSharp7.cs:233:13:233:23 | [false] ... is ... | CSharp7.cs:233:13:233:33 | [true] ... && ... |
| CSharp7.cs:233:13:233:23 | [true] ... is ... | CSharp7.cs:233:13:233:33 | [false] ... && ... |
| CSharp7.cs:233:13:233:23 | [true] ... is ... | CSharp7.cs:233:13:233:33 | [true] ... && ... |
| CSharp7.cs:233:18:233:23 | Int32 i1 | CSharp7.cs:233:18:233:23 | SSA def(i1) |
@@ -338,6 +340,8 @@
| CSharp7.cs:297:35:297:35 | access to local variable x | CSharp7.cs:297:40:297:44 | Int32 y |
| CSharp7.cs:297:35:297:35 | access to local variable x | CSharp7.cs:297:49:297:49 | access to local variable x |
| CSharp7.cs:297:35:297:44 | [false] ... is ... | CSharp7.cs:297:25:297:44 | [false] ... && ... |
| CSharp7.cs:297:35:297:44 | [false] ... is ... | CSharp7.cs:297:25:297:44 | [true] ... && ... |
| CSharp7.cs:297:35:297:44 | [true] ... is ... | CSharp7.cs:297:25:297:44 | [false] ... && ... |
| CSharp7.cs:297:35:297:44 | [true] ... is ... | CSharp7.cs:297:25:297:44 | [true] ... && ... |
| CSharp7.cs:297:40:297:44 | Int32 y | CSharp7.cs:297:40:297:44 | SSA def(y) |
| CSharp7.cs:297:40:297:44 | SSA def(y) | CSharp7.cs:299:31:299:31 | access to local variable y |

View File

@@ -24,6 +24,7 @@
| ExactCallable.cs:15:25:15:35 | Run`2 | ExactCallable.cs:172:21:172:33 | MethodWithOut |
| ExactCallable.cs:15:25:15:35 | Run`2 | ExactCallable.cs:177:21:177:34 | MethodWithOut2 |
| ExactCallable.cs:182:21:182:22 | M1 | ExactCallable.cs:187:21:187:22 | M2 |
| TypeFlow.cs:3:7:3:14 | <object initializer> | TypeFlow.cs:22:20:22:22 | set_Prop |
| TypeFlow.cs:5:5:5:12 | TypeFlow | TypeFlow.cs:24:10:24:12 | Run |
| TypeFlow.cs:24:10:24:12 | Run | TypeFlow.cs:12:29:12:34 | Method |
| TypeFlow.cs:24:10:24:12 | Run | TypeFlow.cs:17:30:17:35 | Method |

View File

@@ -56,11 +56,11 @@ gvn
| StructuralComparison.cs:3:14:3:18 | this access | (kind:Expr(12),false,Class) |
| StructuralComparison.cs:3:14:3:18 | {...} | (kind:Stmt(1)) |
| StructuralComparison.cs:5:26:5:26 | access to field x | (kind:Expr(16),true,x) |
| StructuralComparison.cs:5:26:5:26 | this access | (kind:Expr(12)) |
| StructuralComparison.cs:5:26:5:26 | this access | (kind:Expr(12),false,Class) |
| StructuralComparison.cs:5:26:5:30 | ... = ... | ((kind:Expr(16),true,x) :: (0 :: (kind:Expr(63)))) |
| StructuralComparison.cs:5:30:5:30 | 0 | 0 |
| StructuralComparison.cs:6:26:6:26 | access to field y | (kind:Expr(16),true,y) |
| StructuralComparison.cs:6:26:6:26 | this access | (kind:Expr(12)) |
| StructuralComparison.cs:6:26:6:26 | this access | (kind:Expr(12),false,Class) |
| StructuralComparison.cs:6:26:6:30 | ... = ... | ((kind:Expr(16),true,y) :: (1 :: (kind:Expr(63)))) |
| StructuralComparison.cs:6:30:6:30 | 1 | 1 |
| StructuralComparison.cs:8:24:8:24 | 0 | 0 |

View File

@@ -33,6 +33,11 @@ public class LogForgingHandler : IHttpHandler
Microsoft.Extensions.Logging.ILogger logger2 = null;
// BAD: Logged as-is
logger2.LogError(username); // $ Alert
// GOOD: uses safe extension method that sanitizes internally
logger.WarnSafe(username + " logged in");
// BAD: uses unsafe extension method that does not sanitize
logger.WarnUnsafe(username + " logged in");
}
public bool IsReusable
@@ -43,3 +48,16 @@ public class LogForgingHandler : IHttpHandler
}
}
}
static class UserLoggerExtensions
{
public static void WarnSafe(this ILogger logger, string message)
{
logger.Warn(message.ReplaceLineEndings(""));
}
public static void WarnUnsafe(this ILogger logger, string message)
{
logger.Warn(message); // $ Alert
}
}

View File

@@ -2,14 +2,18 @@
| LogForging.cs:21:21:21:43 | ... + ... | LogForging.cs:18:27:18:49 | access to property QueryString : NameValueCollection | LogForging.cs:21:21:21:43 | ... + ... | This log entry depends on a $@. | LogForging.cs:18:27:18:49 | access to property QueryString | user-provided value |
| LogForging.cs:31:50:31:72 | ... + ... | LogForging.cs:18:27:18:49 | access to property QueryString : NameValueCollection | LogForging.cs:31:50:31:72 | ... + ... | This log entry depends on a $@. | LogForging.cs:18:27:18:49 | access to property QueryString | user-provided value |
| LogForging.cs:35:26:35:33 | access to local variable username | LogForging.cs:18:27:18:49 | access to property QueryString : NameValueCollection | LogForging.cs:35:26:35:33 | access to local variable username | This log entry depends on a $@. | LogForging.cs:18:27:18:49 | access to property QueryString | user-provided value |
| LogForging.cs:61:21:61:27 | access to parameter message | LogForging.cs:18:27:18:49 | access to property QueryString : NameValueCollection | LogForging.cs:61:21:61:27 | access to parameter message | This log entry depends on a $@. | LogForging.cs:18:27:18:49 | access to property QueryString | user-provided value |
| LogForgingAsp.cs:17:21:17:43 | ... + ... | LogForgingAsp.cs:13:32:13:39 | username : String | LogForgingAsp.cs:17:21:17:43 | ... + ... | This log entry depends on a $@. | LogForgingAsp.cs:13:32:13:39 | username | user-provided value |
edges
| LogForging.cs:18:16:18:23 | access to local variable username : String | LogForging.cs:21:21:21:43 | ... + ... | provenance | |
| LogForging.cs:18:16:18:23 | access to local variable username : String | LogForging.cs:31:50:31:72 | ... + ... | provenance | |
| LogForging.cs:18:16:18:23 | access to local variable username : String | LogForging.cs:35:26:35:33 | access to local variable username | provenance | |
| LogForging.cs:18:16:18:23 | access to local variable username : String | LogForging.cs:40:27:40:49 | ... + ... : String | provenance | |
| LogForging.cs:18:27:18:49 | access to property QueryString : NameValueCollection | LogForging.cs:18:16:18:23 | access to local variable username : String | provenance | |
| LogForging.cs:18:27:18:49 | access to property QueryString : NameValueCollection | LogForging.cs:18:27:18:61 | access to indexer : String | provenance | MaD:1 |
| LogForging.cs:18:27:18:61 | access to indexer : String | LogForging.cs:18:16:18:23 | access to local variable username : String | provenance | |
| LogForging.cs:40:27:40:49 | ... + ... : String | LogForging.cs:59:63:59:69 | message : String | provenance | |
| LogForging.cs:59:63:59:69 | message : String | LogForging.cs:61:21:61:27 | access to parameter message | provenance | |
| LogForgingAsp.cs:13:32:13:39 | username : String | LogForgingAsp.cs:17:21:17:43 | ... + ... | provenance | |
models
| 1 | Summary: System.Collections.Specialized; NameValueCollection; false; get_Item; (System.String); ; Argument[this]; ReturnValue; taint; df-generated |
@@ -20,6 +24,9 @@ nodes
| LogForging.cs:21:21:21:43 | ... + ... | semmle.label | ... + ... |
| LogForging.cs:31:50:31:72 | ... + ... | semmle.label | ... + ... |
| LogForging.cs:35:26:35:33 | access to local variable username | semmle.label | access to local variable username |
| LogForging.cs:40:27:40:49 | ... + ... : String | semmle.label | ... + ... : String |
| LogForging.cs:59:63:59:69 | message : String | semmle.label | message : String |
| LogForging.cs:61:21:61:27 | access to parameter message | semmle.label | access to parameter message |
| LogForgingAsp.cs:13:32:13:39 | username : String | semmle.label | username : String |
| LogForgingAsp.cs:17:21:17:43 | ... + ... | semmle.label | ... + ... |
subpaths

View File

@@ -406,7 +406,7 @@ 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.
- **kind**: Kind of source to add. See the section on source kinds for a list of supported kinds.
Example:
@@ -553,7 +553,16 @@ Kinds
Source kinds
~~~~~~~~~~~~
See documentation below for :ref:`Threat models <threat-models-javascript>`.
- **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 <threat-models-javascript>`.
Sink kinds
~~~~~~~~~~

View File

@@ -0,0 +1,121 @@
.. _codeql-cli-2.24.3:
==========================
CodeQL 2.24.3 (2026-03-05)
==========================
.. 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 <https://github.blog/tag/code-scanning/>`__, `relevant GitHub Changelog updates <https://github.blog/changelog/label/application-security/>`__, `changes in the CodeQL extension for Visual Studio Code <https://marketplace.visualstudio.com/items/GitHub.vscode-codeql/changelog>`__, and the `CodeQL Action changelog <https://github.com/github/codeql-action/blob/main/CHANGELOG.md>`__.
Security Coverage
-----------------
CodeQL 2.24.3 runs a total of 491 security queries when configured with the Default suite (covering 166 CWE). The Extended suite enables an additional 135 queries (covering 35 more CWE).
CodeQL CLI
----------
Bug Fixes
~~~~~~~~~
* Fixed a race condition that could cause flaky failures in overlay CodeQL tests. Test extraction now skips :code:`*.testproj` directories by name, preventing interference from concurrently cleaned-up test databases.
* Fixed spurious "OOPS" warnings that could appear in help output for commands using mutually exclusive option groups, such as :code:`codeql query run`.
Query Packs
-----------
Minor Analysis Improvements
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Java/Kotlin
"""""""""""
* The Java extractor and QL libraries now support Java 26.
* Java analysis now selects the Java version to use informed by Maven POM files across all project modules. It also tries to use Java 17 or higher for all Maven projects if possible, for improved build compatibility.
Rust
""""
* The macro resolution metric has been removed from :code:`rust/diagnostic/database-quality`. This metric was found to be an unreliable indicator of database quality in many cases, leading to false alarms on the tool status page.
Language Libraries
------------------
Bug Fixes
~~~~~~~~~
C/C++
"""""
* The :code:`allowInterproceduralFlow` predicate of must-flow data flow configurations now correctly handles direct recursion.
C#
""
* Fixed an issue where the body of a partial member could be extracted twice. When both a *defining* and an *implementing* declaration exist, only the *implementing* declaration is now extracted.
Breaking Changes
~~~~~~~~~~~~~~~~
C/C++
"""""
* CodeQL version 2.24.2 accidentally introduced a syntactical breaking change to :code:`BarrierGuard<...>::getAnIndirectBarrierNode` and :code:`InstructionBarrierGuard<...>::getAnIndirectBarrierNode`. These breaking changes have now been reverted so that the original code compiles again.
* :code:`MustFlow`, the inter-procedural must-flow data flow analysis library, has been re-worked to use parameterized modules. Like in the case of data flow and taint tracking, instead of extending the :code:`MustFlowConfiguration` class, the user should now implement a module with the :code:`MustFlow::ConfigSig` signature, and instantiate the :code:`MustFlow::Global` parameterized module with the implemented module.
Python
""""""
* The :code:`Metrics` library no longer contains code that depends on the points-to analysis. The removed functionality has instead been moved to the :code:`LegacyPointsTo` module, to classes like :code:`ModuleMetricsWithPointsTo` etc. If you depend on any of these classes, you must now remember to import :code:`LegacyPointsTo`, and use the appropriate types in order to use the points-to-based functionality.
Major Analysis Improvements
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Python
""""""
* The CodeQL Python libraries have been updated to be compatible with overlay evaluation. This should result in a significant speedup on analyses for which a base database already exists. Note that it may be necessary to add :code:`overlay[local?] module;` to user-managed libraries that extend classes that are now marked as :code:`overlay[local]`.
Minor Analysis Improvements
~~~~~~~~~~~~~~~~~~~~~~~~~~~
C/C++
"""""
* Refactored the "Year field changed using an arithmetic operation without checking for leap year" query (:code:`cpp/leap-year/unchecked-after-arithmetic-year-modification`) to address large numbers of false positive results.
C#
""
* C# 14: Added support for partial events.
* C# 14: Added support for the :code:`field` keyword in properties.
Java/Kotlin
"""""""""""
* Some modelling which previously only worked for Java EE packages beginning with "javax" will now also work for Java EE packages beginning with "jakarta" as well. This may lead to some alert changes.
JavaScript/TypeScript
"""""""""""""""""""""
* Added support for React components wrapped by :code:`observer` from :code:`mobx-react` and :code:`mobx-react-lite`.
Python
""""""
* Added new full SSRF sanitization barrier from the new AntiSSRF library.
* When a guard such as :code:`isSafe(x)` is defined, we now also automatically handle :code:`isSafe(x) == true` and :code:`isSafe(x) != false`.
Ruby
""""
* We now track taint flow through :code:`Shellwords.escape` and :code:`Shellwords.shellescape` for all queries except command injection, for which they are sanitizers.
Rust
""""
* Added support for neutral models (:code:`extensible: neutralModel`) to control where generated source, sink and flow summary models apply.

Some files were not shown because too many files have changed in this diff Show More