mirror of
https://github.com/github/codeql.git
synced 2026-04-04 22:58:16 +02:00
Merge branch 'main' into jeongsoolee09/add-getIndirectionIndex
This commit is contained in:
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -45,3 +45,5 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
exclude-paths:
|
||||
- "misc/bazel/registry/**"
|
||||
|
||||
78
.github/workflows/compile-queries.yml
vendored
78
.github/workflows/compile-queries.yml
vendored
@@ -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
|
||||
12
MODULE.bazel
12
MODULE.bazel
@@ -15,14 +15,14 @@ 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.6.1")
|
||||
bazel_dep(name = "bazel_skylib", version = "1.8.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")
|
||||
@@ -30,7 +30,7 @@ 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.17.0.bcr.2")
|
||||
bazel_dep(name = "rules_rust", version = "0.68.1.codeql.1")
|
||||
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)
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.4.30
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.4.29
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
3
actions/ql/lib/change-notes/released/0.4.30.md
Normal file
3
actions/ql/lib/change-notes/released/0.4.30.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.4.30
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.4.29
|
||||
lastReleaseVersion: 0.4.30
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/actions-all
|
||||
version: 0.4.30-dev
|
||||
version: 0.4.31-dev
|
||||
library: true
|
||||
warnOnImplicitThis: true
|
||||
dependencies:
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.6.22
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.6.21
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
3
actions/ql/src/change-notes/released/0.6.22.md
Normal file
3
actions/ql/src/change-notes/released/0.6.22.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.6.22
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.6.21
|
||||
lastReleaseVersion: 0.6.22
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 8.0.0
|
||||
lastReleaseVersion: 8.0.1
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.5.13
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.5.12
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -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() + "'."
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
25
cpp/ql/src/Telemetry/DatabaseQuality.qll
Normal file
25
cpp/ql/src/Telemetry/DatabaseQuality.qll
Normal 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>;
|
||||
25
cpp/ql/src/Telemetry/ExtractorInformation.ql
Normal file
25
cpp/ql/src/Telemetry/ExtractorInformation.ql
Normal 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
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
3
cpp/ql/src/change-notes/released/1.5.13.md
Normal file
3
cpp/ql/src/change-notes/released/1.5.13.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 1.5.13
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.5.12
|
||||
lastReleaseVersion: 1.5.13
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 1.5.13-dev
|
||||
version: 1.5.14-dev
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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'. |
|
||||
|
||||
@@ -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'. |
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
// defines type size_t plausibly
|
||||
typedef unsigned long size_t;
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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 * |
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.7.61
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.7.60
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.7.61
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.7.60
|
||||
lastReleaseVersion: 1.7.61
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-all
|
||||
version: 1.7.61-dev
|
||||
version: 1.7.62-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.7.61
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.7.60
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.7.61
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.7.60
|
||||
lastReleaseVersion: 1.7.61
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-queries
|
||||
version: 1.7.61-dev
|
||||
version: 1.7.62-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* C# 14: Added support for partial constructors.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added post-update nodes for struct-type arguments, allowing data flow out of method calls via those arguments.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added reverse taint flow from implicit conversion operator calls to their arguments.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added `System.Net.WebSockets::ReceiveAsync` as a remote flow source.
|
||||
9
csharp/ql/lib/change-notes/released/5.4.9.md
Normal file
9
csharp/ql/lib/change-notes/released/5.4.9.md
Normal 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.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 5.4.8
|
||||
lastReleaseVersion: 5.4.9
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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`. */
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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`. */
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.6.4
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.6.3
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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).
|
||||
3
csharp/ql/src/change-notes/released/1.6.4.md
Normal file
3
csharp/ql/src/change-notes/released/1.6.4.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 1.6.4
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.6.3
|
||||
lastReleaseVersion: 1.6.4
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-queries
|
||||
version: 1.6.4-dev
|
||||
version: 1.6.5-dev
|
||||
groups:
|
||||
- csharp
|
||||
- queries
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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
|
||||
~~~~~~~~~~
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.0.44
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.0.43
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.0.44
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.0.43
|
||||
lastReleaseVersion: 1.0.44
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql-go-consistency-queries
|
||||
version: 1.0.44-dev
|
||||
version: 1.0.45-dev
|
||||
groups:
|
||||
- go
|
||||
- queries
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
## 7.0.2
|
||||
|
||||
### 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.
|
||||
|
||||
## 7.0.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
## 7.0.2
|
||||
|
||||
### 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.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 7.0.1
|
||||
lastReleaseVersion: 7.0.2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/go-all
|
||||
version: 7.0.2-dev
|
||||
version: 7.0.3-dev
|
||||
groups: go
|
||||
dbscheme: go.dbscheme
|
||||
extractor: go
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.5.8
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.5.7
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* scripting vulnerability.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 6.1
|
||||
* @security-severity 7.8
|
||||
* @precision high
|
||||
* @id go/html-template-escaping-bypass-xss
|
||||
* @tags security
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* a cross-site scripting vulnerability.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 6.1
|
||||
* @security-severity 7.8
|
||||
* @precision high
|
||||
* @id go/reflected-xss
|
||||
* @tags security
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* a stored cross-site scripting vulnerability.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 6.1
|
||||
* @security-severity 7.8
|
||||
* @precision low
|
||||
* @id go/stored-xss
|
||||
* @tags security
|
||||
|
||||
@@ -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 medium
|
||||
* @id go/log-injection
|
||||
* @tags security
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: queryMetadata
|
||||
---
|
||||
* The `@security-severity` metadata of `go/log-injection` has been reduced from 7.8 (high) to 6.1 (medium).
|
||||
* The `@security-severity` metadata of `go/html-template-escaping-bypass-xss`, `go/reflected-xss` and `go/stored-xss` has been increased from 6.1 (medium) to 7.8 (high).
|
||||
3
go/ql/src/change-notes/released/1.5.8.md
Normal file
3
go/ql/src/change-notes/released/1.5.8.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 1.5.8
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.5.7
|
||||
lastReleaseVersion: 1.5.8
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/go-queries
|
||||
version: 1.5.8-dev
|
||||
version: 1.5.9-dev
|
||||
groups:
|
||||
- go
|
||||
- queries
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
## 9.0.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* The Java control flow graph (CFG) implementation has been completely
|
||||
rewritten. The CFG now includes additional nodes to more accurately represent
|
||||
certain constructs. This also means that any existing code that implicitly
|
||||
relies on very specific details about the CFG may need to be updated.
|
||||
The CFG now only includes the nodes that are reachable from the entry point.
|
||||
Additionally, the following breaking changes have been made:
|
||||
- `ControlFlowNode.asCall` has been removed - use `Call.getControlFlowNode` instead.
|
||||
- `ControlFlowNode.getEnclosingStmt` has been removed.
|
||||
- `ControlFlow::ExprNode` has been removed.
|
||||
- `ControlFlow::StmtNode` has been removed.
|
||||
- `ControlFlow::Node` has been removed - this was merely an alias of
|
||||
`ControlFlowNode`, which is still available.
|
||||
- Previously deprecated predicates on `BasicBlock` have been removed.
|
||||
|
||||
### 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.
|
||||
* The class `Assignment` now extends `BinaryExpr`. Uses of `BinaryExpr` may in some cases need slight adjustment.
|
||||
|
||||
## 8.1.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The class `Assignment` now extends `BinaryExpr`. Uses of `BinaryExpr` may in some cases need slight adjustment.
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
category: breaking
|
||||
---
|
||||
## 9.0.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* The Java control flow graph (CFG) implementation has been completely
|
||||
rewritten. The CFG now includes additional nodes to more accurately represent
|
||||
certain constructs. This also means that any existing code that implicitly
|
||||
@@ -14,3 +15,8 @@ category: breaking
|
||||
- `ControlFlow::Node` has been removed - this was merely an alias of
|
||||
`ControlFlowNode`, which is still available.
|
||||
- Previously deprecated predicates on `BasicBlock` have been removed.
|
||||
|
||||
### 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.
|
||||
* The class `Assignment` now extends `BinaryExpr`. Uses of `BinaryExpr` may in some cases need slight adjustment.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user