mirror of
https://github.com/github/codeql.git
synced 2026-05-26 17:11:24 +02:00
Compare commits
7 Commits
atm-experi
...
nickrolfe/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73b34729e1 | ||
|
|
d5667bd6e6 | ||
|
|
fa0a34975e | ||
|
|
e666df77b8 | ||
|
|
47bcf69acd | ||
|
|
bffe7f8ce4 | ||
|
|
ecbdb661e9 |
@@ -6,8 +6,6 @@
|
||||
"*/ql/examples/qlpack.yml",
|
||||
"*/ql/consistency-queries/qlpack.yml",
|
||||
"cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml",
|
||||
"go/ql/config/legacy-support/qlpack.yml",
|
||||
"go/build/codeql-extractor-go/codeql-extractor.yml",
|
||||
"javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml",
|
||||
"javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/qlpack.yml",
|
||||
"javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml",
|
||||
@@ -17,7 +15,6 @@
|
||||
"misc/legacy-support/*/qlpack.yml",
|
||||
"misc/suite-helpers/qlpack.yml",
|
||||
"ruby/extractor-pack/codeql-extractor.yml",
|
||||
"swift/extractor-pack/codeql-extractor.yml",
|
||||
"ql/extractor-pack/codeql-extractor.yml"
|
||||
],
|
||||
"versionPolicies": {
|
||||
|
||||
9
.gitattributes
vendored
9
.gitattributes
vendored
@@ -39,7 +39,6 @@
|
||||
*.py text
|
||||
*.lua text
|
||||
*.expected text
|
||||
*.go text
|
||||
|
||||
# Explicitly set a bunch of known extensions to binary, because Git < 2.10 will treat
|
||||
# `* text=auto eol=lf` as `* text eol=lf`
|
||||
@@ -53,14 +52,6 @@
|
||||
java/ql/test/stubs/**/*.java linguist-generated=true
|
||||
java/ql/test/experimental/stubs/**/*.java linguist-generated=true
|
||||
|
||||
# Force git not to modify line endings for go or html files under the go/ql directory
|
||||
go/ql/**/*.go -text
|
||||
go/ql/**/*.html -text
|
||||
# Force git not to modify line endings for go dbschemes
|
||||
go/*.dbscheme -text
|
||||
# Preserve unusual line ending from codeql-go merge
|
||||
go/extractor/opencsv/CSVReader.java -text
|
||||
|
||||
# For some languages, upgrade script testing references really old dbscheme
|
||||
# files from legacy upgrades that have CRLF line endings. Since upgrade
|
||||
# resolution relies on object hashes, we must suppress line ending conversion
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "codeql-query-format",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^((.*) would change by autoformatting\\.)$",
|
||||
"file": 2,
|
||||
"message": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "codeql-syntax-check",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^((ERROR|WARNING): .* \\((.*):(\\d+),(\\d+)-\\d+\\))$",
|
||||
"message": 1,
|
||||
"file": 3,
|
||||
"line": 4,
|
||||
"col": 5,
|
||||
"severity": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
14
.github/problem-matchers/codeql-test-run.json
vendored
14
.github/problem-matchers/codeql-test-run.json
vendored
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "codeql-test-run",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "(\\[.*\\] FAILED\\((RESULT|COMPILATION)\\) (.*))$",
|
||||
"file": 3,
|
||||
"message": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
13
.github/problem-matchers/make.json
vendored
13
.github/problem-matchers/make.json
vendored
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "make",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^(make: \\*\\*\\* .*)$",
|
||||
"message": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
161
.github/workflows/go-tests.yml
vendored
161
.github/workflows/go-tests.yml
vendored
@@ -1,161 +0,0 @@
|
||||
name: "Go: Run Tests"
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "go/**"
|
||||
- .github/workflows/go-tests.yml
|
||||
jobs:
|
||||
|
||||
test-linux:
|
||||
name: Test Linux (Ubuntu)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.18.1
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18.1
|
||||
id: go
|
||||
|
||||
- name: Set up CodeQL CLI
|
||||
run: |
|
||||
echo "Removing old CodeQL Directory..."
|
||||
rm -rf $HOME/codeql
|
||||
echo "Done"
|
||||
cd $HOME
|
||||
echo "Downloading CodeQL CLI..."
|
||||
LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | sort --version-sort | grep -v beta | tail -1)
|
||||
gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-linux64.zip "$LATEST"
|
||||
echo "Done"
|
||||
echo "Unpacking CodeQL CLI..."
|
||||
unzip -q codeql-linux64.zip
|
||||
rm -f codeql-linux64.zip
|
||||
echo "Done"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Enable problem matchers in repository
|
||||
shell: bash
|
||||
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cd go
|
||||
env PATH=$PATH:$HOME/codeql make
|
||||
|
||||
- name: Check that all QL and Go code is autoformatted
|
||||
run: |
|
||||
cd go
|
||||
env PATH=$PATH:$HOME/codeql make check-formatting
|
||||
|
||||
- name: Compile qhelp files to markdown
|
||||
run: |
|
||||
cd go
|
||||
env PATH=$PATH:$HOME/codeql QHELP_OUT_DIR=qhelp-out make qhelp-to-markdown
|
||||
|
||||
- name: Upload qhelp markdown
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: qhelp-markdown
|
||||
path: go/qhelp-out/**/*.md
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
cd go
|
||||
env PATH=$PATH:$HOME/codeql make test
|
||||
|
||||
test-mac:
|
||||
name: Test MacOS
|
||||
runs-on: macOS-latest
|
||||
steps:
|
||||
- name: Set up Go 1.18.1
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18.1
|
||||
id: go
|
||||
|
||||
- name: Set up CodeQL CLI
|
||||
run: |
|
||||
echo "Removing old CodeQL Directory..."
|
||||
rm -rf $HOME/codeql
|
||||
echo "Done"
|
||||
cd $HOME
|
||||
echo "Downloading CodeQL CLI..."
|
||||
LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | sort --version-sort | grep -v beta | tail -1)
|
||||
gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-osx64.zip "$LATEST"
|
||||
echo "Done"
|
||||
echo "Unpacking CodeQL CLI..."
|
||||
unzip -q codeql-osx64.zip
|
||||
rm -f codeql-osx64.zip
|
||||
echo "Done"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Enable problem matchers in repository
|
||||
shell: bash
|
||||
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cd go
|
||||
env PATH=$PATH:$HOME/codeql make
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
cd go
|
||||
env PATH=$PATH:$HOME/codeql make test
|
||||
|
||||
test-win:
|
||||
name: Test Windows
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- name: Set up Go 1.18.1
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18.1
|
||||
id: go
|
||||
|
||||
- name: Set up CodeQL CLI
|
||||
run: |
|
||||
echo "Removing old CodeQL Directory..."
|
||||
rm -rf $HOME/codeql
|
||||
echo "Done"
|
||||
cd "$HOME"
|
||||
echo "Downloading CodeQL CLI..."
|
||||
LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | sort --version-sort | grep -v beta | tail -1)
|
||||
gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-win64.zip "$LATEST"
|
||||
echo "Done"
|
||||
echo "Unpacking CodeQL CLI..."
|
||||
unzip -q -o codeql-win64.zip
|
||||
unzip -q -o codeql-win64.zip codeql/codeql.exe
|
||||
rm -f codeql-win64.zip
|
||||
echo "Done"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
shell:
|
||||
bash
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Enable problem matchers in repository
|
||||
shell: bash
|
||||
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
$Env:Path += ";$HOME\codeql"
|
||||
cd go
|
||||
make
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
$Env:Path += ";$HOME\codeql"
|
||||
cd go
|
||||
make test
|
||||
2
.github/workflows/ql-for-ql-build.yml
vendored
2
.github/workflows/ql-for-ql-build.yml
vendored
@@ -140,7 +140,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
folder: [cpp, csharp, java, javascript, python, ql, ruby, swift, go]
|
||||
folder: [cpp, csharp, java, javascript, python, ql, ruby, swift]
|
||||
|
||||
needs:
|
||||
- package
|
||||
|
||||
@@ -19,6 +19,7 @@ jobs:
|
||||
matrix:
|
||||
repo:
|
||||
- github/codeql
|
||||
- github/codeql-go
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -76,7 +77,7 @@ jobs:
|
||||
path: stats
|
||||
- run: |
|
||||
python -m pip install --user lxml
|
||||
find stats -name 'stats.xml' -print0 | sort -z | xargs -0 python ruby/scripts/merge_stats.py --output ql/ql/src/ql.dbscheme.stats --normalise ql_tokeninfo
|
||||
find stats -name 'stats.xml' -print0 | sort -z | xargs -0 python ql/scripts/merge_stats.py --output ql/ql/src/ql.dbscheme.stats --normalise ql_tokeninfo
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ql.dbscheme.stats
|
||||
|
||||
5
.github/workflows/query-list.yml
vendored
5
.github/workflows/query-list.yml
vendored
@@ -20,6 +20,11 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: codeql
|
||||
- name: Clone github/codeql-go
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: 'github/codeql-go'
|
||||
path: codeql-go
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
|
||||
3
.github/workflows/swift-codegen.yml
vendored
3
.github/workflows/swift-codegen.yml
vendored
@@ -25,7 +25,8 @@ jobs:
|
||||
git diff --exit-code --stat HEAD
|
||||
- name: Generate C++ files
|
||||
run: |
|
||||
bazel run //swift/codegen:cppcodegen -- --cpp-output=$PWD/swift-generated-headers
|
||||
bazel run //swift/codegen:trapgen -- --cpp-output=$PWD/swift-generated-headers
|
||||
bazel run //swift/codegen:cppgen -- --cpp-output=$PWD/swift-generated-headers
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: swift-generated-headers
|
||||
|
||||
13
.gitignore
vendored
13
.gitignore
vendored
@@ -9,7 +9,6 @@
|
||||
# qltest projects and artifacts
|
||||
*/ql/test/**/*.testproj
|
||||
*/ql/test/**/*.actual
|
||||
*/ql/test/**/go.sum
|
||||
|
||||
# Visual studio temporaries, except a file used by QL4VS
|
||||
.vs/*
|
||||
@@ -43,15 +42,3 @@ csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json
|
||||
|
||||
# CLion project files
|
||||
/.clwb
|
||||
|
||||
# Go build artifacts
|
||||
go/build/*
|
||||
|
||||
# Go binaries
|
||||
go/tools/bin
|
||||
go/tools/linux64
|
||||
go/tools/osx64
|
||||
go/tools/win64
|
||||
go/tools/tokenizer.jar
|
||||
go/main
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ path_classifiers:
|
||||
test:
|
||||
- csharp/ql/src
|
||||
- csharp/ql/test
|
||||
- go/ql/test
|
||||
- javascript/extractor/parser-tests
|
||||
- javascript/extractor/tests
|
||||
- javascript/ql/src
|
||||
@@ -14,9 +13,6 @@ path_classifiers:
|
||||
- python/ql/src
|
||||
- python/ql/test
|
||||
|
||||
example:
|
||||
- go/ql/src
|
||||
|
||||
queries:
|
||||
- include: "*"
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/cpp/ @github/codeql-c-analysis
|
||||
/csharp/ @github/codeql-csharp
|
||||
/go/ @github/codeql-go
|
||||
/java/ @github/codeql-java
|
||||
/javascript/ @github/codeql-javascript
|
||||
/python/ @github/codeql-python
|
||||
@@ -38,7 +37,6 @@
|
||||
|
||||
# Workflows
|
||||
/.github/workflows/ @github/codeql-ci-reviewers
|
||||
/.github/workflows/go-* @github/codeql-go
|
||||
/.github/workflows/js-ml-tests.yml @github/codeql-ml-powered-queries-reviewers
|
||||
/.github/workflows/ql-for-ql-* @github/codeql-ql-for-ql-reviewers
|
||||
/.github/workflows/ruby-* @github/codeql-ruby
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# CodeQL
|
||||
|
||||
This open source repository contains the standard CodeQL libraries and queries that power [GitHub Advanced Security](https://github.com/features/security/code) and the other application security products that [GitHub](https://github.com/features/security/) makes available to its customers worldwide.
|
||||
This open source repository contains the standard CodeQL libraries and queries that power [GitHub Advanced Security](https://github.com/features/security/code) and the other application security products that [GitHub](https://github.com/features/security/) makes available to its customers worldwide. For the queries, libraries, and extractor that power Go analysis, visit the [CodeQL for Go repository](https://github.com/github/codeql-go).
|
||||
|
||||
## How do I learn CodeQL and run queries?
|
||||
|
||||
|
||||
@@ -22,15 +22,13 @@
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForLibraries.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll"
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForLibraries.qll"
|
||||
],
|
||||
"DataFlow Java/C++/C#/Python Common": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll",
|
||||
@@ -38,8 +36,7 @@
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplCommon.qll"
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll"
|
||||
],
|
||||
"TaintTracking::Configuration Java/C++/C#/Python": [
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
@@ -60,8 +57,7 @@
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/tainttracking4/TaintTrackingImpl.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttrackingforlibraries/TaintTrackingImpl.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/tainttracking1/TaintTrackingImpl.qll"
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttrackingforlibraries/TaintTrackingImpl.qll"
|
||||
],
|
||||
"DataFlow Java/C++/C#/Python Consistency checks": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
@@ -69,8 +65,7 @@
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplConsistency.qll"
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll"
|
||||
],
|
||||
"DataFlow Java/C# Flow Summaries": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll",
|
||||
@@ -463,8 +458,7 @@
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll",
|
||||
"csharp/ql/lib/semmle/code/cil/internal/SsaImplCommon.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/SsaImplCommon.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll"
|
||||
],
|
||||
"CryptoAlgorithms Python/JS/Ruby": [
|
||||
"javascript/ql/lib/semmle/javascript/security/CryptoAlgorithms.qll",
|
||||
@@ -506,8 +500,7 @@
|
||||
],
|
||||
"CFG": [
|
||||
"csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll",
|
||||
"ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll",
|
||||
"swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplShared.qll"
|
||||
"ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll"
|
||||
],
|
||||
"TypeTracker": [
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll",
|
||||
@@ -560,9 +553,5 @@
|
||||
"HttpToFileAccessCustomizations JS/Ruby": [
|
||||
"javascript/ql/lib/semmle/javascript/security/dataflow/HttpToFileAccessCustomizations.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/HttpToFileAccessCustomizations.qll"
|
||||
],
|
||||
"Typo database": [
|
||||
"javascript/ql/src/Expressions/TypoDatabase.qll",
|
||||
"ql/ql/src/codeql_ql/style/TypoDatabase.qll"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
description: Add relation for tracking C++ braced initializers
|
||||
compatibility: full
|
||||
braced_initialisers.rel: delete
|
||||
@@ -1,21 +1,3 @@
|
||||
## 0.2.3
|
||||
|
||||
### New Features
|
||||
|
||||
* An `isBraced` predicate was added to the `Initializer` class which holds when a C++ braced initializer was used in the initialization.
|
||||
|
||||
## 0.2.2
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* The `AnalysedString` class in the `StringAnalysis` module has been replaced with `AnalyzedString`, to follow our style guide. The old name still exists as a deprecated alias.
|
||||
|
||||
### New Features
|
||||
|
||||
* A `getInitialization` predicate was added to the `ConstexprIfStmt`, `IfStmt`, and `SwitchStmt` classes that yields the C++17-style initializer of the `if` or `switch` statement when it exists.
|
||||
|
||||
## 0.2.1
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
* A `getInitialization` predicate was added to the `ConstexprIfStmt`, `IfStmt`, and `SwitchStmt` classes that yields the C++17-style initializer of the `if` or `switch` statement when it exists.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
* The `AnalysedString` class in the `StringAnalysis` module has been replaced with `AnalyzedString`, to follow our style guide. The old name still exists as a deprecated alias.
|
||||
@@ -1 +0,0 @@
|
||||
## 0.2.1
|
||||
@@ -1,9 +0,0 @@
|
||||
## 0.2.2
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* The `AnalysedString` class in the `StringAnalysis` module has been replaced with `AnalyzedString`, to follow our style guide. The old name still exists as a deprecated alias.
|
||||
|
||||
### New Features
|
||||
|
||||
* A `getInitialization` predicate was added to the `ConstexprIfStmt`, `IfStmt`, and `SwitchStmt` classes that yields the C++17-style initializer of the `if` or `switch` statement when it exists.
|
||||
@@ -1,5 +0,0 @@
|
||||
## 0.2.3
|
||||
|
||||
### New Features
|
||||
|
||||
* An `isBraced` predicate was added to the `Initializer` class which holds when a C++ braced initializer was used in the initialization.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.2.3
|
||||
lastReleaseVersion: 0.2.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.2.3
|
||||
version: 0.2.1-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -31,7 +31,7 @@ class Field extends MemberVariable {
|
||||
int getByteOffset() { fieldoffsets(underlyingElement(this), result, _) }
|
||||
|
||||
/**
|
||||
* Gets the byte offset within `mostDerivedClass` of each occurrence of this
|
||||
* Gets the byte offset within `mostDerivedClass` of each occurence of this
|
||||
* field within `mostDerivedClass` itself or a base class subobject of
|
||||
* `mostDerivedClass`.
|
||||
* Note that for fields of virtual base classes, and non-virtual base classes
|
||||
|
||||
@@ -51,7 +51,4 @@ class Initializer extends ControlFlowNode, @initialiser {
|
||||
override Function getControlFlowScope() { result = this.getExpr().getEnclosingFunction() }
|
||||
|
||||
override Stmt getEnclosingStmt() { result = this.getExpr().getEnclosingStmt() }
|
||||
|
||||
/** Holds if the initializer used the C++ braced initializer notation. */
|
||||
predicate isBraced() { braced_initialisers(underlyingElement(this)) }
|
||||
}
|
||||
|
||||
@@ -872,7 +872,7 @@ class FormatLiteral extends Literal {
|
||||
|
||||
private Type getConversionType1(int n) {
|
||||
exists(string cnv | cnv = this.getConversionChar(n) |
|
||||
cnv = ["d", "i"] and
|
||||
cnv.regexpMatch("d|i") and
|
||||
result = this.getIntegralConversion(n) and
|
||||
not result.getUnderlyingType().(IntegralType).isExplicitlySigned() and
|
||||
not result.getUnderlyingType().(IntegralType).isExplicitlyUnsigned()
|
||||
@@ -912,7 +912,7 @@ class FormatLiteral extends Literal {
|
||||
|
||||
private Type getConversionType2(int n) {
|
||||
exists(string cnv | cnv = this.getConversionChar(n) |
|
||||
cnv = ["o", "u", "x", "X"] and
|
||||
cnv.regexpMatch("o|u|x|X") and
|
||||
result = this.getIntegralConversion(n) and
|
||||
result.getUnderlyingType().(IntegralType).isUnsigned()
|
||||
)
|
||||
@@ -920,7 +920,7 @@ class FormatLiteral extends Literal {
|
||||
|
||||
private Type getConversionType3(int n) {
|
||||
exists(string cnv | cnv = this.getConversionChar(n) |
|
||||
cnv = ["a", "A", "e", "E", "f", "F", "g", "G"] and result = this.getFloatingPointConversion(n)
|
||||
cnv.regexpMatch("a|A|e|E|f|F|g|G") and result = this.getFloatingPointConversion(n)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -970,7 +970,7 @@ private predicate subEdge(Pos p1, Node n1, Node n2, Pos p2) {
|
||||
private predicate subEdgeIncludingDestructors(Pos p1, Node n1, Node n2, Pos p2) {
|
||||
subEdge(p1, n1, n2, p2)
|
||||
or
|
||||
// If `n1` has sub-nodes to accommodate destructors, but there are none to be
|
||||
// If `n1` has sub-nodes to accomodate destructors, but there are none to be
|
||||
// called, connect the "before destructors" node directly to the "after
|
||||
// destructors" node. For performance, only do this when the nodes exist.
|
||||
exists(Pos afterDtors | afterDtors.isAfterDestructors() | subEdge(afterDtors, n1, _, _)) and
|
||||
|
||||
@@ -216,9 +216,10 @@ private module LambdaFlow {
|
||||
or
|
||||
// jump step
|
||||
exists(Node mid, DataFlowType t0 |
|
||||
revLambdaFlow(lambdaCall, kind, mid, t0, _, _, lastCall) and
|
||||
revLambdaFlow(lambdaCall, kind, mid, t0, _, _, _) and
|
||||
toReturn = false and
|
||||
toJump = true
|
||||
toJump = true and
|
||||
lastCall = TDataFlowCallNone()
|
||||
|
|
||||
jumpStepCached(node, mid) and
|
||||
t = t0
|
||||
@@ -304,7 +305,7 @@ cached
|
||||
private module Cached {
|
||||
/**
|
||||
* If needed, call this predicate from `DataFlowImplSpecific.qll` in order to
|
||||
* force a stage-dependency on the `DataFlowImplCommon.qll` stage and thereby
|
||||
* force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby
|
||||
* collapsing the two stages.
|
||||
*/
|
||||
cached
|
||||
@@ -788,31 +789,24 @@ private module Cached {
|
||||
cached
|
||||
predicate readSet(Node node1, ContentSet c, Node node2) { readStep(node1, c, node2) }
|
||||
|
||||
cached
|
||||
predicate storeSet(
|
||||
Node node1, ContentSet c, Node node2, DataFlowType contentType, DataFlowType containerType
|
||||
) {
|
||||
storeStep(node1, c, node2) and
|
||||
contentType = getNodeDataFlowType(node1) and
|
||||
containerType = getNodeDataFlowType(node2)
|
||||
or
|
||||
exists(Node n1, Node n2 |
|
||||
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
|
||||
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
||||
|
|
||||
argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1)
|
||||
or
|
||||
readSet(n2, c, n1) and
|
||||
contentType = getNodeDataFlowType(n1) and
|
||||
containerType = getNodeDataFlowType(n2)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate store(
|
||||
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
|
||||
) {
|
||||
exists(ContentSet cs |
|
||||
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
|
||||
exists(ContentSet cs | c = cs.getAStoreContent() |
|
||||
storeStep(node1, cs, node2) and
|
||||
contentType = getNodeDataFlowType(node1) and
|
||||
containerType = getNodeDataFlowType(node2)
|
||||
or
|
||||
exists(Node n1, Node n2 |
|
||||
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
|
||||
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
||||
|
|
||||
argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, cs, contentType), n1)
|
||||
or
|
||||
readSet(n2, cs, n1) and
|
||||
contentType = getNodeDataFlowType(n1) and
|
||||
containerType = getNodeDataFlowType(n2)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -549,7 +549,7 @@ module FlowVar_internal {
|
||||
bb = this.(Loop).getStmt() and
|
||||
v = this.getARelevantVariable()
|
||||
or
|
||||
this.reachesWithoutAssignment(pragma[only_bind_out](bb.getAPredecessor()), v) and
|
||||
this.reachesWithoutAssignment(bb.getAPredecessor(), v) and
|
||||
this.bbInLoop(bb)
|
||||
) and
|
||||
not assignsToVar(bb, v)
|
||||
|
||||
@@ -216,9 +216,10 @@ private module LambdaFlow {
|
||||
or
|
||||
// jump step
|
||||
exists(Node mid, DataFlowType t0 |
|
||||
revLambdaFlow(lambdaCall, kind, mid, t0, _, _, lastCall) and
|
||||
revLambdaFlow(lambdaCall, kind, mid, t0, _, _, _) and
|
||||
toReturn = false and
|
||||
toJump = true
|
||||
toJump = true and
|
||||
lastCall = TDataFlowCallNone()
|
||||
|
|
||||
jumpStepCached(node, mid) and
|
||||
t = t0
|
||||
@@ -304,7 +305,7 @@ cached
|
||||
private module Cached {
|
||||
/**
|
||||
* If needed, call this predicate from `DataFlowImplSpecific.qll` in order to
|
||||
* force a stage-dependency on the `DataFlowImplCommon.qll` stage and thereby
|
||||
* force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby
|
||||
* collapsing the two stages.
|
||||
*/
|
||||
cached
|
||||
@@ -788,31 +789,24 @@ private module Cached {
|
||||
cached
|
||||
predicate readSet(Node node1, ContentSet c, Node node2) { readStep(node1, c, node2) }
|
||||
|
||||
cached
|
||||
predicate storeSet(
|
||||
Node node1, ContentSet c, Node node2, DataFlowType contentType, DataFlowType containerType
|
||||
) {
|
||||
storeStep(node1, c, node2) and
|
||||
contentType = getNodeDataFlowType(node1) and
|
||||
containerType = getNodeDataFlowType(node2)
|
||||
or
|
||||
exists(Node n1, Node n2 |
|
||||
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
|
||||
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
||||
|
|
||||
argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1)
|
||||
or
|
||||
readSet(n2, c, n1) and
|
||||
contentType = getNodeDataFlowType(n1) and
|
||||
containerType = getNodeDataFlowType(n2)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate store(
|
||||
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
|
||||
) {
|
||||
exists(ContentSet cs |
|
||||
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
|
||||
exists(ContentSet cs | c = cs.getAStoreContent() |
|
||||
storeStep(node1, cs, node2) and
|
||||
contentType = getNodeDataFlowType(node1) and
|
||||
containerType = getNodeDataFlowType(node2)
|
||||
or
|
||||
exists(Node n1, Node n2 |
|
||||
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
|
||||
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
||||
|
|
||||
argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, cs, contentType), n1)
|
||||
or
|
||||
readSet(n2, cs, n1) and
|
||||
contentType = getNodeDataFlowType(n1) and
|
||||
containerType = getNodeDataFlowType(n2)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ private module Cached {
|
||||
* along the chain of addresses computed by `StoreNodeInstr.getInner` to identify field writes
|
||||
* and call `storeStep` accordingly (i.e., for an expression like `a.b.c = x`, we visit `c`, then
|
||||
* `b`, then `a`).
|
||||
* 2. Flow is transferred from a `WriteSideEffectInstruction` to a `StoreNodeOperand` after flow
|
||||
* 2. Flow is transfered from a `WriteSideEffectInstruction` to a `StoreNodeOperand` after flow
|
||||
* returns to a caller. Flow will then proceed to the defining instruction of the operand (because
|
||||
* the `StoreNodeInstr` computed by `StoreNodeOperand.getInner()` is the `StoreNode` containing
|
||||
* the defining instruction), and then along the chain computed by `StoreNodeInstr.getInner` like
|
||||
|
||||
@@ -67,7 +67,7 @@ class DefaultEdge extends EdgeKind, TDefaultEdge {
|
||||
|
||||
/**
|
||||
* A "case" edge, representing the successor of a `Switch` instruction when the
|
||||
* the condition value matches a corresponding `case` label.
|
||||
* the condition value matches a correponding `case` label.
|
||||
*/
|
||||
class CaseEdge extends EdgeKind, TCaseEdge {
|
||||
string minValue;
|
||||
|
||||
@@ -34,7 +34,7 @@ class ValueNumber extends TValueNumber {
|
||||
final Instruction getAnInstruction() { this = valueNumber(result) }
|
||||
|
||||
/**
|
||||
* Gets one of the instructions that was assigned this value number. The chosen instruction is
|
||||
* Gets one of the instructions that was assigned this value number. The chosen instuction is
|
||||
* deterministic but arbitrary. Intended for use only in debugging.
|
||||
*/
|
||||
final Instruction getExampleInstruction() {
|
||||
|
||||
@@ -1005,7 +1005,7 @@ predicate canReuseSsaForMemoryResult(Instruction instruction) {
|
||||
deprecated predicate canReuseSSAForMemoryResult = canReuseSsaForMemoryResult/1;
|
||||
|
||||
/**
|
||||
* Expose some of the internal predicates to PrintSSA.qll. We do this by publicly importing those modules in the
|
||||
* Expose some of the internal predicates to PrintSSA.qll. We do this by publically importing those modules in the
|
||||
* `DebugSSA` module, which is then imported by PrintSSA.
|
||||
*/
|
||||
module DebugSsa {
|
||||
|
||||
@@ -34,7 +34,7 @@ class ValueNumber extends TValueNumber {
|
||||
final Instruction getAnInstruction() { this = valueNumber(result) }
|
||||
|
||||
/**
|
||||
* Gets one of the instructions that was assigned this value number. The chosen instruction is
|
||||
* Gets one of the instructions that was assigned this value number. The chosen instuction is
|
||||
* deterministic but arbitrary. Intended for use only in debugging.
|
||||
*/
|
||||
final Instruction getExampleInstruction() {
|
||||
|
||||
@@ -34,7 +34,7 @@ class ValueNumber extends TValueNumber {
|
||||
final Instruction getAnInstruction() { this = valueNumber(result) }
|
||||
|
||||
/**
|
||||
* Gets one of the instructions that was assigned this value number. The chosen instruction is
|
||||
* Gets one of the instructions that was assigned this value number. The chosen instuction is
|
||||
* deterministic but arbitrary. Intended for use only in debugging.
|
||||
*/
|
||||
final Instruction getExampleInstruction() {
|
||||
|
||||
@@ -1005,7 +1005,7 @@ predicate canReuseSsaForMemoryResult(Instruction instruction) {
|
||||
deprecated predicate canReuseSSAForMemoryResult = canReuseSsaForMemoryResult/1;
|
||||
|
||||
/**
|
||||
* Expose some of the internal predicates to PrintSSA.qll. We do this by publicly importing those modules in the
|
||||
* Expose some of the internal predicates to PrintSSA.qll. We do this by publically importing those modules in the
|
||||
* `DebugSSA` module, which is then imported by PrintSSA.
|
||||
*/
|
||||
module DebugSsa {
|
||||
|
||||
@@ -59,7 +59,7 @@ predicate isInsecureEncryption(string name) { name.regexpMatch(getInsecureAlgori
|
||||
/**
|
||||
* Holds if there is additional evidence that `name` looks like it might be
|
||||
* related to operations with an encyption algorithm, besides the name of a
|
||||
* specific algorithm. This can be used in conjunction with
|
||||
* specific algorithm. This can be used in conjuction with
|
||||
* `isInsecureEncryption` to produce a stronger heuristic.
|
||||
*/
|
||||
bindingset[name]
|
||||
|
||||
@@ -1436,10 +1436,6 @@ initialisers(
|
||||
int location: @location_expr ref
|
||||
);
|
||||
|
||||
braced_initialisers(
|
||||
int init: @initialiser ref
|
||||
);
|
||||
|
||||
/**
|
||||
* An ancestor for the expression, for cases in which we cannot
|
||||
* otherwise find the expression's parent.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,2 +0,0 @@
|
||||
description: Add relation for tracking C++ braced initializers
|
||||
compatibility: backwards
|
||||
@@ -1,18 +1,3 @@
|
||||
## 0.1.4
|
||||
|
||||
## 0.1.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The "XML external entity expansion" (`cpp/external-entity-expansion`) query precision has been increased to `high`.
|
||||
* The `cpp/unused-local-variable` no longer ignores functions that include `if` and `switch` statements with C++17-style initializers.
|
||||
|
||||
## 0.1.2
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The "XML external entity expansion" (`cpp/external-entity-expansion`) query has been extended to support a broader selection of XML libraries and interfaces.
|
||||
|
||||
## 0.1.1
|
||||
|
||||
### New Queries
|
||||
|
||||
@@ -57,10 +57,10 @@ class ExtractionProblem extends TExtractionProblem {
|
||||
/** Gets the problem message for this problem. */
|
||||
string getProblemMessage() { none() }
|
||||
|
||||
/** Gets the file this problem occurred in. */
|
||||
/** Gets the file this problem occured in. */
|
||||
File getFile() { none() }
|
||||
|
||||
/** Gets the location this problem occurred in. */
|
||||
/** Gets the location this problem occured in. */
|
||||
Location getLocation() { none() }
|
||||
|
||||
/** Gets the SARIF severity of this problem. */
|
||||
|
||||
@@ -57,10 +57,10 @@ class ExtractionError extends TExtractionError {
|
||||
/** Gets the error message for this error. */
|
||||
string getErrorMessage() { none() }
|
||||
|
||||
/** Gets the file this error occurred in. */
|
||||
/** Gets the file this error occured in. */
|
||||
File getFile() { none() }
|
||||
|
||||
/** Gets the location this error occurred in. */
|
||||
/** Gets the location this error occured in. */
|
||||
Location getLocation() { none() }
|
||||
|
||||
/** Gets the SARIF severity of this error. */
|
||||
|
||||
@@ -19,7 +19,7 @@ predicate whitelist(Function f) {
|
||||
"nearbyintl", "rint", "rintf", "rintl", "round", "roundf", "roundl", "trunc", "truncf",
|
||||
"truncl"
|
||||
] or
|
||||
f.getName().matches("\\_\\_builtin\\_%")
|
||||
f.getName().matches("__builtin_%")
|
||||
}
|
||||
|
||||
predicate whitelistPow(FunctionCall fc) {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* @deprecated This query is deprecated, use
|
||||
* Potentially overrunning write (`cpp/overrunning-write`) and
|
||||
* Potentially overrunning write with float to string conversion
|
||||
* (`cpp/overrunning-write-with-float`) instead.
|
||||
* (`cpp/overrunning-write-with-float) instead.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
/**
|
||||
* Models the libxml2 XML library.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import XML
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
/**
|
||||
* A call to a `libxml2` function that parses XML.
|
||||
*/
|
||||
class Libxml2ParseCall extends FunctionCall {
|
||||
int optionsArg;
|
||||
|
||||
Libxml2ParseCall() {
|
||||
exists(string fname | this.getTarget().getName() = fname |
|
||||
fname = "xmlCtxtUseOptions" and optionsArg = 1
|
||||
or
|
||||
fname = "xmlReadFile" and optionsArg = 2
|
||||
or
|
||||
fname = ["xmlCtxtReadFile", "xmlParseInNodeContext", "xmlReadDoc", "xmlReadFd"] and
|
||||
optionsArg = 3
|
||||
or
|
||||
fname = ["xmlCtxtReadDoc", "xmlCtxtReadFd", "xmlReadMemory"] and optionsArg = 4
|
||||
or
|
||||
fname = ["xmlCtxtReadMemory", "xmlReadIO"] and optionsArg = 5
|
||||
or
|
||||
fname = "xmlCtxtReadIO" and optionsArg = 6
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument that specifies `xmlParserOption`s.
|
||||
*/
|
||||
Expr getOptions() { result = this.getArgument(optionsArg) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An `xmlParserOption` for `libxml2` that is considered unsafe.
|
||||
*/
|
||||
class Libxml2BadOption extends EnumConstant {
|
||||
Libxml2BadOption() { this.getName() = ["XML_PARSE_NOENT", "XML_PARSE_DTDLOAD"] }
|
||||
}
|
||||
|
||||
/**
|
||||
* The libxml2 XML library.
|
||||
*/
|
||||
class LibXml2Library extends XmlLibrary {
|
||||
LibXml2Library() { this = "LibXml2Library" }
|
||||
|
||||
override predicate configurationSource(DataFlow::Node node, string flowstate) {
|
||||
// source is an `options` argument on a libxml2 parse call that specifies
|
||||
// at least one unsafe option.
|
||||
//
|
||||
// note: we don't need to track an XML object for libxml2, so we don't
|
||||
// really need data flow. Nevertheless we jam it into this configuration,
|
||||
// with matching sources and sinks. This allows results to be presented by
|
||||
// the same query, in a consistent way as other results with flow paths.
|
||||
exists(Libxml2ParseCall call, Expr options |
|
||||
options = call.getOptions() and
|
||||
node.asExpr() = options and
|
||||
flowstate = "libxml2" and
|
||||
exists(Libxml2BadOption opt |
|
||||
globalValueNumber(options).getAnExpr().getValue().toInt().bitAnd(opt.getValue().toInt()) !=
|
||||
0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate configurationSink(DataFlow::Node node, string flowstate) {
|
||||
// sink is the `options` argument on a `libxml2` parse call.
|
||||
exists(Libxml2ParseCall call, Expr options |
|
||||
options = call.getOptions() and
|
||||
node.asExpr() = options and
|
||||
flowstate = "libxml2"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/**
|
||||
* Provides a abstract classes for modeling XML libraries. This design is
|
||||
* currently specialized for the purposes of the XXE query.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
import Xerces
|
||||
import Libxml2
|
||||
|
||||
/**
|
||||
* A flow state representing a possible configuration of an XML object.
|
||||
*/
|
||||
abstract class XxeFlowState extends DataFlow::FlowState {
|
||||
bindingset[this]
|
||||
XxeFlowState() { any() } // required characteristic predicate
|
||||
}
|
||||
|
||||
/**
|
||||
* An XML library or interface.
|
||||
*/
|
||||
abstract class XmlLibrary extends string {
|
||||
bindingset[this]
|
||||
XmlLibrary() { any() } // required characteristic predicate
|
||||
|
||||
/**
|
||||
* Holds if `node` is the source node for a potentially unsafe configuration
|
||||
* object for this XML library, along with `flowstate` representing its
|
||||
* initial state.
|
||||
*/
|
||||
abstract predicate configurationSource(DataFlow::Node node, string flowstate);
|
||||
|
||||
/**
|
||||
* Holds if `node` is the sink node where an unsafe configuration object is
|
||||
* used to interpret XML.
|
||||
*/
|
||||
abstract predicate configurationSink(DataFlow::Node node, string flowstate);
|
||||
}
|
||||
|
||||
/**
|
||||
* An `Expr` that changes the configuration of an XML object, transforming the
|
||||
* `XxeFlowState` that flows through it.
|
||||
*/
|
||||
abstract class XxeFlowStateTransformer extends Expr {
|
||||
/**
|
||||
* Gets the flow state that `flowstate` is transformed into.
|
||||
*
|
||||
* Due to limitations of the implementation the transformation defined by this
|
||||
* predicate must be idempotent, that is, for any input `x` it must be that:
|
||||
* ```
|
||||
* transform(transform(x)) = transform(x)
|
||||
* ```
|
||||
*/
|
||||
abstract XxeFlowState transform(XxeFlowState flowstate);
|
||||
}
|
||||
@@ -13,8 +13,344 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import XML
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
import DataFlow::PathGraph
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
/**
|
||||
* A flow state representing a possible configuration of an XML object.
|
||||
*/
|
||||
abstract class XXEFlowState extends DataFlow::FlowState {
|
||||
bindingset[this]
|
||||
XXEFlowState() { any() } // required characteristic predicate
|
||||
}
|
||||
|
||||
/**
|
||||
* An `Expr` that changes the configuration of an XML object, transforming the
|
||||
* `XXEFlowState` that flows through it.
|
||||
*/
|
||||
abstract class XXEFlowStateTransformer extends Expr {
|
||||
/**
|
||||
* Gets the flow state that `flowstate` is transformed into.
|
||||
*
|
||||
* Due to limitations of the implementation the transformation defined by this
|
||||
* predicate must be idempotent, that is, for any input `x` it must be that:
|
||||
* ```
|
||||
* transform(tranform(x)) = tranform(x)
|
||||
* ```
|
||||
*/
|
||||
abstract XXEFlowState transform(XXEFlowState flowstate);
|
||||
}
|
||||
|
||||
/**
|
||||
* The `AbstractDOMParser` class.
|
||||
*/
|
||||
class AbstractDOMParserClass extends Class {
|
||||
AbstractDOMParserClass() { this.hasName("AbstractDOMParser") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `XercesDOMParser` class.
|
||||
*/
|
||||
class XercesDOMParserClass extends Class {
|
||||
XercesDOMParserClass() { this.hasName("XercesDOMParser") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `DOMLSParser` class.
|
||||
*/
|
||||
class DomLSParserClass extends Class {
|
||||
DomLSParserClass() { this.hasName("DOMLSParser") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `SAXParser` class.
|
||||
*/
|
||||
class SaxParserClass extends Class {
|
||||
SaxParserClass() { this.hasName("SAXParser") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `SAX2XMLReader` class.
|
||||
*/
|
||||
class Sax2XmlReader extends Class {
|
||||
Sax2XmlReader() { this.hasName("SAX2XMLReader") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a valid flow state for `AbstractDOMParser` or `SAXParser` flow.
|
||||
*
|
||||
* These flow states take the form `Xerces-A-B`, where:
|
||||
* - A is 1 if `setDisableDefaultEntityResolution` is `true`, 0 otherwise.
|
||||
* - B is 1 if `setCreateEntityReferenceNodes` is `true`, 0 otherwise.
|
||||
*/
|
||||
predicate encodeXercesFlowState(
|
||||
string flowstate, int disabledDefaultEntityResolution, int createEntityReferenceNodes
|
||||
) {
|
||||
flowstate = "Xerces-0-0" and
|
||||
disabledDefaultEntityResolution = 0 and
|
||||
createEntityReferenceNodes = 0
|
||||
or
|
||||
flowstate = "Xerces-0-1" and
|
||||
disabledDefaultEntityResolution = 0 and
|
||||
createEntityReferenceNodes = 1
|
||||
or
|
||||
flowstate = "Xerces-1-0" and
|
||||
disabledDefaultEntityResolution = 1 and
|
||||
createEntityReferenceNodes = 0
|
||||
or
|
||||
flowstate = "Xerces-1-1" and
|
||||
disabledDefaultEntityResolution = 1 and
|
||||
createEntityReferenceNodes = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state representing the configuration of an `AbstractDOMParser` or
|
||||
* `SAXParser` object.
|
||||
*/
|
||||
class XercesFlowState extends XXEFlowState {
|
||||
XercesFlowState() { encodeXercesFlowState(this, _, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state transformer for a call to
|
||||
* `AbstractDOMParser.setDisableDefaultEntityResolution` or
|
||||
* `SAXParser.setDisableDefaultEntityResolution`. Transforms the flow
|
||||
* state through the qualifier according to the setting in the parameter.
|
||||
*/
|
||||
class DisableDefaultEntityResolutionTransformer extends XXEFlowStateTransformer {
|
||||
Expr newValue;
|
||||
|
||||
DisableDefaultEntityResolutionTransformer() {
|
||||
exists(Call call, Function f |
|
||||
call.getTarget() = f and
|
||||
(
|
||||
f.getDeclaringType() instanceof AbstractDOMParserClass or
|
||||
f.getDeclaringType() instanceof SaxParserClass
|
||||
) and
|
||||
f.hasName("setDisableDefaultEntityResolution") and
|
||||
this = call.getQualifier() and
|
||||
newValue = call.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
final override XXEFlowState transform(XXEFlowState flowstate) {
|
||||
exists(int createEntityReferenceNodes |
|
||||
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
|
||||
(
|
||||
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
|
||||
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
|
||||
or
|
||||
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
|
||||
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state transformer for a call to
|
||||
* `AbstractDOMParser.setCreateEntityReferenceNodes`. Transforms the flow
|
||||
* state through the qualifier according to the setting in the parameter.
|
||||
*/
|
||||
class CreateEntityReferenceNodesTransformer extends XXEFlowStateTransformer {
|
||||
Expr newValue;
|
||||
|
||||
CreateEntityReferenceNodesTransformer() {
|
||||
exists(Call call, Function f |
|
||||
call.getTarget() = f and
|
||||
f.getClassAndName("setCreateEntityReferenceNodes") instanceof AbstractDOMParserClass and
|
||||
this = call.getQualifier() and
|
||||
newValue = call.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
final override XXEFlowState transform(XXEFlowState flowstate) {
|
||||
exists(int disabledDefaultEntityResolution |
|
||||
encodeXercesFlowState(flowstate, disabledDefaultEntityResolution, _) and
|
||||
(
|
||||
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
|
||||
encodeXercesFlowState(result, disabledDefaultEntityResolution, 1)
|
||||
or
|
||||
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
|
||||
encodeXercesFlowState(result, disabledDefaultEntityResolution, 0)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `XMLUni.fgXercesDisableDefaultEntityResolution` constant.
|
||||
*/
|
||||
class FeatureDisableDefaultEntityResolution extends Variable {
|
||||
FeatureDisableDefaultEntityResolution() {
|
||||
this.getName() = "fgXercesDisableDefaultEntityResolution" and
|
||||
this.getDeclaringType().getName() = "XMLUni"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state transformer for a call to `SAX2XMLReader.setFeature`
|
||||
* specifying the feature `XMLUni::fgXercesDisableDefaultEntityResolution`.
|
||||
* Transforms the flow state through the qualifier according to this setting.
|
||||
*/
|
||||
class SetFeatureTransformer extends XXEFlowStateTransformer {
|
||||
Expr newValue;
|
||||
|
||||
SetFeatureTransformer() {
|
||||
exists(Call call, Function f |
|
||||
call.getTarget() = f and
|
||||
f.getClassAndName("setFeature") instanceof Sax2XmlReader and
|
||||
this = call.getQualifier() and
|
||||
globalValueNumber(call.getArgument(0)).getAnExpr().(VariableAccess).getTarget() instanceof
|
||||
FeatureDisableDefaultEntityResolution and
|
||||
newValue = call.getArgument(1)
|
||||
)
|
||||
}
|
||||
|
||||
final override XXEFlowState transform(XXEFlowState flowstate) {
|
||||
exists(int createEntityReferenceNodes |
|
||||
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
|
||||
(
|
||||
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
|
||||
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
|
||||
or
|
||||
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
|
||||
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `DOMLSParser.getDomConfig` function.
|
||||
*/
|
||||
class GetDomConfig extends Function {
|
||||
GetDomConfig() { this.getClassAndName("getDomConfig") instanceof DomLSParserClass }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `DOMConfiguration.setParameter` function.
|
||||
*/
|
||||
class DomConfigurationSetParameter extends Function {
|
||||
DomConfigurationSetParameter() {
|
||||
this.getClassAndName("setParameter").getName() = "DOMConfiguration"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state transformer for a call to `DOMConfiguration.setParameter`
|
||||
* specifying the feature `XMLUni::fgXercesDisableDefaultEntityResolution`.
|
||||
* This is a slightly more complex transformer because the qualifier is a
|
||||
* `DOMConfiguration` pointer returned by `DOMLSParser.getDomConfig` - and it
|
||||
* is *that* qualifier we want to transform the flow state of.
|
||||
*/
|
||||
class DomConfigurationSetParameterTransformer extends XXEFlowStateTransformer {
|
||||
Expr newValue;
|
||||
|
||||
DomConfigurationSetParameterTransformer() {
|
||||
exists(FunctionCall getDomConfigCall, FunctionCall setParameterCall |
|
||||
// this is the qualifier of a call to `DOMLSParser.getDomConfig`.
|
||||
getDomConfigCall.getTarget() instanceof GetDomConfig and
|
||||
this = getDomConfigCall.getQualifier() and
|
||||
// `setParameterCall` is a call to `setParameter` on the return value of
|
||||
// the same call to `DOMLSParser.getDomConfig`.
|
||||
setParameterCall.getTarget() instanceof DomConfigurationSetParameter and
|
||||
globalValueNumber(setParameterCall.getQualifier()).getAnExpr() = getDomConfigCall and
|
||||
// the parameter being set is
|
||||
// `XMLUni::fgXercesDisableDefaultEntityResolution`.
|
||||
globalValueNumber(setParameterCall.getArgument(0)).getAnExpr().(VariableAccess).getTarget()
|
||||
instanceof FeatureDisableDefaultEntityResolution and
|
||||
// the value being set is `newValue`.
|
||||
newValue = setParameterCall.getArgument(1)
|
||||
)
|
||||
}
|
||||
|
||||
final override XXEFlowState transform(XXEFlowState flowstate) {
|
||||
exists(int createEntityReferenceNodes |
|
||||
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
|
||||
(
|
||||
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
|
||||
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
|
||||
or
|
||||
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
|
||||
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `AbstractDOMParser.parse`, `DOMLSParserClass.parse`, `SAXParser.parse`
|
||||
* or `SAX2XMLReader.parse` method.
|
||||
*/
|
||||
class ParseFunction extends Function {
|
||||
ParseFunction() {
|
||||
this.getClassAndName("parse") instanceof AbstractDOMParserClass or
|
||||
this.getClassAndName("parse") instanceof DomLSParserClass or
|
||||
this.getClassAndName("parse") instanceof SaxParserClass or
|
||||
this.getClassAndName("parse") instanceof Sax2XmlReader
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `createLSParser` function that returns a newly created `DOMLSParser`
|
||||
* object.
|
||||
*/
|
||||
class CreateLSParser extends Function {
|
||||
CreateLSParser() {
|
||||
this.hasName("createLSParser") and
|
||||
this.getUnspecifiedType().(PointerType).getBaseType() instanceof DomLSParserClass // returns a `DOMLSParser *`.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `createXMLReader` function that returns a newly created `SAX2XMLReader`
|
||||
* object.
|
||||
*/
|
||||
class CreateXmlReader extends Function {
|
||||
CreateXmlReader() {
|
||||
this.hasName("createXMLReader") and
|
||||
this.getUnspecifiedType().(PointerType).getBaseType() instanceof Sax2XmlReader // returns a `SAX2XMLReader *`.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a `libxml2` function that parses XML.
|
||||
*/
|
||||
class Libxml2ParseCall extends FunctionCall {
|
||||
int optionsArg;
|
||||
|
||||
Libxml2ParseCall() {
|
||||
exists(string fname | this.getTarget().getName() = fname |
|
||||
fname = "xmlCtxtUseOptions" and optionsArg = 1
|
||||
or
|
||||
fname = "xmlReadFile" and optionsArg = 2
|
||||
or
|
||||
fname = ["xmlCtxtReadFile", "xmlParseInNodeContext", "xmlReadDoc", "xmlReadFd"] and
|
||||
optionsArg = 3
|
||||
or
|
||||
fname = ["xmlCtxtReadDoc", "xmlCtxtReadFd", "xmlReadMemory"] and optionsArg = 4
|
||||
or
|
||||
fname = ["xmlCtxtReadMemory", "xmlReadIO"] and optionsArg = 5
|
||||
or
|
||||
fname = "xmlCtxtReadIO" and optionsArg = 6
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument that specifies `xmlParserOption`s.
|
||||
*/
|
||||
Expr getOptions() { result = this.getArgument(optionsArg) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An `xmlParserOption` for `libxml2` that is considered unsafe.
|
||||
*/
|
||||
class Libxml2BadOption extends EnumConstant {
|
||||
Libxml2BadOption() { this.getName() = ["XML_PARSE_NOENT", "XML_PARSE_DTDLOAD"] }
|
||||
}
|
||||
|
||||
/**
|
||||
* A configuration for tracking XML objects and their states.
|
||||
@@ -23,25 +359,85 @@ class XXEConfiguration extends DataFlow::Configuration {
|
||||
XXEConfiguration() { this = "XXEConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node, string flowstate) {
|
||||
any(XmlLibrary l).configurationSource(node, flowstate)
|
||||
// source is the write on `this` of a call to the `XercesDOMParser`
|
||||
// constructor.
|
||||
exists(CallInstruction call |
|
||||
call.getStaticCallTarget() = any(XercesDOMParserClass c).getAConstructor() and
|
||||
node.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() =
|
||||
call.getThisArgument() and
|
||||
encodeXercesFlowState(flowstate, 0, 1) // default configuration
|
||||
)
|
||||
or
|
||||
// source is the result of a call to `createLSParser`.
|
||||
exists(Call call |
|
||||
call.getTarget() instanceof CreateLSParser and
|
||||
call = node.asExpr() and
|
||||
encodeXercesFlowState(flowstate, 0, 1) // default configuration
|
||||
)
|
||||
or
|
||||
// source is the write on `this` of a call to the `SAXParser`
|
||||
// constructor.
|
||||
exists(CallInstruction call |
|
||||
call.getStaticCallTarget() = any(SaxParserClass c).getAConstructor() and
|
||||
node.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() =
|
||||
call.getThisArgument() and
|
||||
encodeXercesFlowState(flowstate, 0, 1) // default configuration
|
||||
)
|
||||
or
|
||||
// source is the result of a call to `createXMLReader`.
|
||||
exists(Call call |
|
||||
call.getTarget() instanceof CreateXmlReader and
|
||||
call = node.asExpr() and
|
||||
encodeXercesFlowState(flowstate, 0, 1) // default configuration
|
||||
)
|
||||
or
|
||||
// source is an `options` argument on a `libxml2` parse call that specifies
|
||||
// at least one unsafe option.
|
||||
//
|
||||
// note: we don't need to track an XML object for `libxml2`, so we don't
|
||||
// really need data flow. Nevertheless we jam it into this configuration,
|
||||
// with matching sources and sinks. This allows results to be presented by
|
||||
// the same query, in a consistent way as other results with flow paths.
|
||||
exists(Libxml2ParseCall call, Expr options |
|
||||
options = call.getOptions() and
|
||||
node.asExpr() = options and
|
||||
flowstate = "libxml2" and
|
||||
exists(Libxml2BadOption opt |
|
||||
globalValueNumber(options).getAnExpr().getValue().toInt().bitAnd(opt.getValue().toInt()) !=
|
||||
0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node node, string flowstate) {
|
||||
any(XmlLibrary l).configurationSink(node, flowstate)
|
||||
// sink is the read of the qualifier of a call to `parse`.
|
||||
exists(Call call |
|
||||
call.getTarget() instanceof ParseFunction and
|
||||
call.getQualifier() = node.asConvertedExpr()
|
||||
) and
|
||||
flowstate instanceof XercesFlowState and
|
||||
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
|
||||
or
|
||||
// sink is the `options` argument on a `libxml2` parse call.
|
||||
exists(Libxml2ParseCall call, Expr options |
|
||||
options = call.getOptions() and
|
||||
node.asExpr() = options and
|
||||
flowstate = "libxml2"
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, string state1, DataFlow::Node node2, string state2
|
||||
) {
|
||||
// create additional flow steps for `XxeFlowStateTransformer`s
|
||||
state2 = node2.asConvertedExpr().(XxeFlowStateTransformer).transform(state1) and
|
||||
// create additional flow steps for `XXEFlowStateTransformer`s
|
||||
state2 = node2.asConvertedExpr().(XXEFlowStateTransformer).transform(state1) and
|
||||
DataFlow::simpleLocalFlowStep(node1, node2)
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node node, string flowstate) {
|
||||
// when the flowstate is transformed at a call node, block the original
|
||||
// flowstate value.
|
||||
node.asConvertedExpr().(XxeFlowStateTransformer).transform(flowstate) != flowstate
|
||||
node.asConvertedExpr().(XXEFlowStateTransformer).transform(flowstate) != flowstate
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,376 +0,0 @@
|
||||
/**
|
||||
* Models the Xerces XML library.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import XML
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
import semmle.code.cpp.ir.IR
|
||||
|
||||
/**
|
||||
* Gets a valid flow state for `AbstractDOMParser` or `SAXParser` flow.
|
||||
*
|
||||
* These flow states take the form `Xerces-A-B`, where:
|
||||
* - A is 1 if `setDisableDefaultEntityResolution` is `true`, 0 otherwise.
|
||||
* - B is 1 if `setCreateEntityReferenceNodes` is `true`, 0 otherwise.
|
||||
*/
|
||||
predicate encodeXercesFlowState(
|
||||
string flowstate, int disabledDefaultEntityResolution, int createEntityReferenceNodes
|
||||
) {
|
||||
flowstate = "Xerces-0-0" and
|
||||
disabledDefaultEntityResolution = 0 and
|
||||
createEntityReferenceNodes = 0
|
||||
or
|
||||
flowstate = "Xerces-0-1" and
|
||||
disabledDefaultEntityResolution = 0 and
|
||||
createEntityReferenceNodes = 1
|
||||
or
|
||||
flowstate = "Xerces-1-0" and
|
||||
disabledDefaultEntityResolution = 1 and
|
||||
createEntityReferenceNodes = 0
|
||||
or
|
||||
flowstate = "Xerces-1-1" and
|
||||
disabledDefaultEntityResolution = 1 and
|
||||
createEntityReferenceNodes = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state representing the configuration of an `AbstractDOMParser` or
|
||||
* `SAXParser` object.
|
||||
*/
|
||||
class XercesFlowState extends XxeFlowState {
|
||||
XercesFlowState() { encodeXercesFlowState(this, _, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `AbstractDOMParser` class.
|
||||
*/
|
||||
class AbstractDomParserClass extends Class {
|
||||
AbstractDomParserClass() { this.hasName("AbstractDOMParser") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `XercesDOMParser` class.
|
||||
*/
|
||||
class XercesDomParserClass extends Class {
|
||||
XercesDomParserClass() { this.hasName("XercesDOMParser") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `XercesDOMParser` interface for the Xerces XML library.
|
||||
*/
|
||||
class XercesDomParserLibrary extends XmlLibrary {
|
||||
XercesDomParserLibrary() { this = "XercesDomParserLibrary" }
|
||||
|
||||
override predicate configurationSource(DataFlow::Node node, string flowstate) {
|
||||
// source is the write on `this` of a call to the `XercesDOMParser`
|
||||
// constructor.
|
||||
exists(CallInstruction call |
|
||||
call.getStaticCallTarget() = any(XercesDomParserClass c).getAConstructor() and
|
||||
node.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() =
|
||||
call.getThisArgument() and
|
||||
encodeXercesFlowState(flowstate, 0, 1) // default configuration
|
||||
)
|
||||
}
|
||||
|
||||
override predicate configurationSink(DataFlow::Node node, string flowstate) {
|
||||
// sink is the read of the qualifier of a call to `AbstractDOMParser.parse`.
|
||||
exists(Call call |
|
||||
call.getTarget().getClassAndName("parse") instanceof AbstractDomParserClass and
|
||||
call.getQualifier() = node.asConvertedExpr()
|
||||
) and
|
||||
flowstate instanceof XercesFlowState and
|
||||
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `DOMLSParser` class.
|
||||
*/
|
||||
class DomLSParserClass extends Class {
|
||||
DomLSParserClass() { this.hasName("DOMLSParser") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `createLSParser` function that returns a newly created `DOMLSParser`
|
||||
* object.
|
||||
*/
|
||||
class CreateLSParser extends Function {
|
||||
CreateLSParser() {
|
||||
this.hasName("createLSParser") and
|
||||
this.getUnspecifiedType().(PointerType).getBaseType() instanceof DomLSParserClass // returns a `DOMLSParser *`.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The createLSParser interface for the Xerces XML library.
|
||||
*/
|
||||
class CreateLSParserLibrary extends XmlLibrary {
|
||||
CreateLSParserLibrary() { this = "CreateLSParserLibrary" }
|
||||
|
||||
override predicate configurationSource(DataFlow::Node node, string flowstate) {
|
||||
// source is the result of a call to `createLSParser`.
|
||||
exists(Call call |
|
||||
call.getTarget() instanceof CreateLSParser and
|
||||
call = node.asExpr() and
|
||||
encodeXercesFlowState(flowstate, 0, 1) // default configuration
|
||||
)
|
||||
}
|
||||
|
||||
override predicate configurationSink(DataFlow::Node node, string flowstate) {
|
||||
// sink is the read of the qualifier of a call to `DOMLSParserClass.parse`.
|
||||
exists(Call call |
|
||||
call.getTarget().getClassAndName("parse") instanceof DomLSParserClass and
|
||||
call.getQualifier() = node.asConvertedExpr()
|
||||
) and
|
||||
flowstate instanceof XercesFlowState and
|
||||
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `SAXParser` class.
|
||||
*/
|
||||
class SaxParserClass extends Class {
|
||||
SaxParserClass() { this.hasName("SAXParser") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `SAX2XMLReader` class.
|
||||
*/
|
||||
class Sax2XmlReader extends Class {
|
||||
Sax2XmlReader() { this.hasName("SAX2XMLReader") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The SAXParser interface for the Xerces XML library.
|
||||
*/
|
||||
class SaxParserLibrary extends XmlLibrary {
|
||||
SaxParserLibrary() { this = "SaxParserLibrary" }
|
||||
|
||||
override predicate configurationSource(DataFlow::Node node, string flowstate) {
|
||||
// source is the write on `this` of a call to the `SAXParser`
|
||||
// constructor.
|
||||
exists(CallInstruction call |
|
||||
call.getStaticCallTarget() = any(SaxParserClass c).getAConstructor() and
|
||||
node.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() =
|
||||
call.getThisArgument() and
|
||||
encodeXercesFlowState(flowstate, 0, 1) // default configuration
|
||||
)
|
||||
}
|
||||
|
||||
override predicate configurationSink(DataFlow::Node node, string flowstate) {
|
||||
// sink is the read of the qualifier of a call to `SAXParser.parse`.
|
||||
exists(Call call |
|
||||
call.getTarget().getClassAndName("parse") instanceof SaxParserClass and
|
||||
call.getQualifier() = node.asConvertedExpr()
|
||||
) and
|
||||
flowstate instanceof XercesFlowState and
|
||||
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `createXMLReader` function that returns a newly created `SAX2XMLReader`
|
||||
* object.
|
||||
*/
|
||||
class CreateXmlReader extends Function {
|
||||
CreateXmlReader() {
|
||||
this.hasName("createXMLReader") and
|
||||
this.getUnspecifiedType().(PointerType).getBaseType() instanceof Sax2XmlReader // returns a `SAX2XMLReader *`.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The SAX2XMLReader interface for the Xerces XML library.
|
||||
*/
|
||||
class Sax2XmlReaderLibrary extends XmlLibrary {
|
||||
Sax2XmlReaderLibrary() { this = "Sax2XmlReaderLibrary" }
|
||||
|
||||
override predicate configurationSource(DataFlow::Node node, string flowstate) {
|
||||
// source is the result of a call to `createXMLReader`.
|
||||
exists(Call call |
|
||||
call.getTarget() instanceof CreateXmlReader and
|
||||
call = node.asExpr() and
|
||||
encodeXercesFlowState(flowstate, 0, 1) // default configuration
|
||||
)
|
||||
}
|
||||
|
||||
override predicate configurationSink(DataFlow::Node node, string flowstate) {
|
||||
// sink is the read of the qualifier of a call to `SAX2XMLReader.parse`.
|
||||
exists(Call call |
|
||||
call.getTarget().getClassAndName("parse") instanceof Sax2XmlReader and
|
||||
call.getQualifier() = node.asConvertedExpr()
|
||||
) and
|
||||
flowstate instanceof XercesFlowState and
|
||||
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state transformer for a call to
|
||||
* `AbstractDOMParser.setDisableDefaultEntityResolution` or
|
||||
* `SAXParser.setDisableDefaultEntityResolution`. Transforms the flow
|
||||
* state through the qualifier according to the setting in the parameter.
|
||||
*/
|
||||
class DisableDefaultEntityResolutionTransformer extends XxeFlowStateTransformer {
|
||||
Expr newValue;
|
||||
|
||||
DisableDefaultEntityResolutionTransformer() {
|
||||
exists(Call call, Function f |
|
||||
call.getTarget() = f and
|
||||
(
|
||||
f.getDeclaringType() instanceof AbstractDomParserClass or
|
||||
f.getDeclaringType() instanceof SaxParserClass
|
||||
) and
|
||||
f.hasName("setDisableDefaultEntityResolution") and
|
||||
this = call.getQualifier() and
|
||||
newValue = call.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
final override XxeFlowState transform(XxeFlowState flowstate) {
|
||||
exists(int createEntityReferenceNodes |
|
||||
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
|
||||
(
|
||||
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
|
||||
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
|
||||
or
|
||||
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
|
||||
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state transformer for a call to
|
||||
* `AbstractDOMParser.setCreateEntityReferenceNodes`. Transforms the flow
|
||||
* state through the qualifier according to the setting in the parameter.
|
||||
*/
|
||||
class CreateEntityReferenceNodesTransformer extends XxeFlowStateTransformer {
|
||||
Expr newValue;
|
||||
|
||||
CreateEntityReferenceNodesTransformer() {
|
||||
exists(Call call, Function f |
|
||||
call.getTarget() = f and
|
||||
f.getClassAndName("setCreateEntityReferenceNodes") instanceof AbstractDomParserClass and
|
||||
this = call.getQualifier() and
|
||||
newValue = call.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
final override XxeFlowState transform(XxeFlowState flowstate) {
|
||||
exists(int disabledDefaultEntityResolution |
|
||||
encodeXercesFlowState(flowstate, disabledDefaultEntityResolution, _) and
|
||||
(
|
||||
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
|
||||
encodeXercesFlowState(result, disabledDefaultEntityResolution, 1)
|
||||
or
|
||||
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
|
||||
encodeXercesFlowState(result, disabledDefaultEntityResolution, 0)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `XMLUni.fgXercesDisableDefaultEntityResolution` constant.
|
||||
*/
|
||||
class FeatureDisableDefaultEntityResolution extends Variable {
|
||||
FeatureDisableDefaultEntityResolution() {
|
||||
this.getName() = "fgXercesDisableDefaultEntityResolution" and
|
||||
this.getDeclaringType().getName() = "XMLUni"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state transformer for a call to `SAX2XMLReader.setFeature`
|
||||
* specifying the feature `XMLUni::fgXercesDisableDefaultEntityResolution`.
|
||||
* Transforms the flow state through the qualifier according to this setting.
|
||||
*/
|
||||
class SetFeatureTransformer extends XxeFlowStateTransformer {
|
||||
Expr newValue;
|
||||
|
||||
SetFeatureTransformer() {
|
||||
exists(Call call, Function f |
|
||||
call.getTarget() = f and
|
||||
f.getClassAndName("setFeature") instanceof Sax2XmlReader and
|
||||
this = call.getQualifier() and
|
||||
globalValueNumber(call.getArgument(0)).getAnExpr().(VariableAccess).getTarget() instanceof
|
||||
FeatureDisableDefaultEntityResolution and
|
||||
newValue = call.getArgument(1)
|
||||
)
|
||||
}
|
||||
|
||||
final override XxeFlowState transform(XxeFlowState flowstate) {
|
||||
exists(int createEntityReferenceNodes |
|
||||
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
|
||||
(
|
||||
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
|
||||
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
|
||||
or
|
||||
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
|
||||
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `DOMLSParser.getDomConfig` function.
|
||||
*/
|
||||
class GetDomConfig extends Function {
|
||||
GetDomConfig() { this.getClassAndName("getDomConfig") instanceof DomLSParserClass }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `DOMConfiguration.setParameter` function.
|
||||
*/
|
||||
class DomConfigurationSetParameter extends Function {
|
||||
DomConfigurationSetParameter() {
|
||||
this.getClassAndName("setParameter").getName() = "DOMConfiguration"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow state transformer for a call to `DOMConfiguration.setParameter`
|
||||
* specifying the feature `XMLUni::fgXercesDisableDefaultEntityResolution`.
|
||||
* This is a slightly more complex transformer because the qualifier is a
|
||||
* `DOMConfiguration` pointer returned by `DOMLSParser.getDomConfig` - and it
|
||||
* is *that* qualifier we want to transform the flow state of.
|
||||
*/
|
||||
class DomConfigurationSetParameterTransformer extends XxeFlowStateTransformer {
|
||||
Expr newValue;
|
||||
|
||||
DomConfigurationSetParameterTransformer() {
|
||||
exists(FunctionCall getDomConfigCall, FunctionCall setParameterCall |
|
||||
// this is the qualifier of a call to `DOMLSParser.getDomConfig`.
|
||||
getDomConfigCall.getTarget() instanceof GetDomConfig and
|
||||
this = getDomConfigCall.getQualifier() and
|
||||
// `setParameterCall` is a call to `setParameter` on the return value of
|
||||
// the same call to `DOMLSParser.getDomConfig`.
|
||||
setParameterCall.getTarget() instanceof DomConfigurationSetParameter and
|
||||
globalValueNumber(setParameterCall.getQualifier()).getAnExpr() = getDomConfigCall and
|
||||
// the parameter being set is
|
||||
// `XMLUni::fgXercesDisableDefaultEntityResolution`.
|
||||
globalValueNumber(setParameterCall.getArgument(0)).getAnExpr().(VariableAccess).getTarget()
|
||||
instanceof FeatureDisableDefaultEntityResolution and
|
||||
// the value being set is `newValue`.
|
||||
newValue = setParameterCall.getArgument(1)
|
||||
)
|
||||
}
|
||||
|
||||
final override XxeFlowState transform(XxeFlowState flowstate) {
|
||||
exists(int createEntityReferenceNodes |
|
||||
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
|
||||
(
|
||||
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
|
||||
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
|
||||
or
|
||||
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
|
||||
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `cpp/unused-local-variable` no longer ignores functions that include `if` and `switch` statements with C++17-style initializers.
|
||||
@@ -1,5 +1,4 @@
|
||||
## 0.1.2
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The "XML external entity expansion" (`cpp/external-entity-expansion`) query has been extended to support a broader selection of XML libraries and interfaces.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The "XML external entity expansion" (`cpp/external-entity-expansion`) query precision has been increased to `high`.
|
||||
@@ -1,6 +0,0 @@
|
||||
## 0.1.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The "XML external entity expansion" (`cpp/external-entity-expansion`) query precision has been increased to `high`.
|
||||
* The `cpp/unused-local-variable` no longer ignores functions that include `if` and `switch` statements with C++17-style initializers.
|
||||
@@ -1 +0,0 @@
|
||||
## 0.1.4
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.1.4
|
||||
lastReleaseVersion: 0.1.1
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
void test()
|
||||
{
|
||||
uint16_t j = 256;
|
||||
char testSubject[122];
|
||||
|
||||
testSubject[j] = 12; // You can use a uint8 here
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>Find access to an array with a Uint16 when the array has a size lower than 256.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>Use a int with a lower bit size instead. For instance in this example use a 8 bit int.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<sample src="WrongUintAccess.cpp" />
|
||||
</example>
|
||||
|
||||
</qhelp>
|
||||
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* @id cpp/wrong-uint-access
|
||||
* @name Wrong Uint
|
||||
* @descripion Acess an array of size lower than 256 with a uint16.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @tags efficiency
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from Variable var, ArrayExpr useExpr, ArrayType defLine, VariableAccess use
|
||||
where
|
||||
var.getUnspecifiedType() = defLine and
|
||||
use = useExpr.getArrayBase() and
|
||||
var = use.getTarget() and
|
||||
(
|
||||
useExpr.getArrayOffset().getType() instanceof UInt16_t or
|
||||
useExpr.getArrayOffset().getType() instanceof UInt32_t or
|
||||
useExpr.getArrayOffset().getType() instanceof UInt64_t
|
||||
) and
|
||||
defLine.getArraySize() <= 256
|
||||
select useExpr,
|
||||
"Using a " + useExpr.getArrayOffset().getType() + " to acess the array $@ of size " +
|
||||
defLine.getArraySize() + ".", var, var.getName()
|
||||
@@ -58,7 +58,7 @@ where
|
||||
// unfortunately cannot use numeric value here because // O_CREAT is defined differently on different OSes:
|
||||
// https://github.com/red/red/blob/92feb0c0d5f91e087ab35fface6906afbf99b603/runtime/definitions.reds#L477-L491
|
||||
// this may introduce false negatives
|
||||
fctmp.getArgument(1).(BitwiseOrExpr).getAChild*().getValueText() = "O_CREAT" or
|
||||
fctmp.getArgument(1).(BitwiseOrExpr).getAChild*().getValueText().matches("O_CREAT") or
|
||||
fctmp.getArgument(1).getValueText().matches("%O_CREAT%")
|
||||
) and
|
||||
fctmp.getNumberOfArguments() = 2 and
|
||||
|
||||
@@ -13,7 +13,7 @@ import cpp
|
||||
|
||||
from Function f
|
||||
where
|
||||
f.getName() = ["atof", "atoi", "atol"] and
|
||||
f.getName().regexpMatch("atof|atoi|atol") and
|
||||
f.getFile().getAbsolutePath().matches("%stdlib.h")
|
||||
select f.getACallToThisFunction(),
|
||||
"AV Rule 23: The library functions atof, atoi and atol from library <stdlib.h> shall not be used."
|
||||
|
||||
@@ -13,7 +13,7 @@ import cpp
|
||||
|
||||
from Function f
|
||||
where
|
||||
f.getName() = ["abort", "exit", "getenv", "system"] and
|
||||
f.getName().regexpMatch("abort|exit|getenv|system") and
|
||||
f.getFile().getAbsolutePath().matches("%stdlib.h")
|
||||
select f.getACallToThisFunction(),
|
||||
"The library functions abort, exit, getenv and system from library <stdlib.h> should not be used."
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 0.1.4
|
||||
version: 0.1.2-dev
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
|
||||
@@ -181,7 +181,7 @@ private string expectationCommentPattern() { result = "\\s*\\$((?:[^/]|/[^/])*)(
|
||||
/**
|
||||
* The possible columns in an expectation comment. The `TDefaultColumn` branch represents the first
|
||||
* column in a comment. This column is not precedeeded by a name. `TNamedColumn(name)` represents a
|
||||
* column containing expected results preceded by the string `name:`.
|
||||
* column containing expected results preceeded by the string `name:`.
|
||||
*/
|
||||
private newtype TColumn =
|
||||
TDefaultColumn() or
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package,sink,source,summary,sink:code,sink:html,sink:remote,sink:sql,sink:xss,source:local,summary:taint,summary:value
|
||||
Dapper,55,,,,,,55,,,,
|
||||
Microsoft.ApplicationBlocks.Data,28,,,,,,28,,,,
|
||||
Microsoft.EntityFrameworkCore,6,,,,,,6,,,,
|
||||
Microsoft.Extensions.Primitives,,,54,,,,,,,54,
|
||||
Microsoft.VisualBasic,,,4,,,,,,,,4
|
||||
MySql.Data.MySqlClient,48,,,,,,48,,,,
|
||||
|
||||
|
@@ -9,6 +9,6 @@ C# framework & library support
|
||||
Framework / library,Package,Flow sources,Taint & value steps,Sinks (total),`CWE-079` :sub:`Cross-site scripting`
|
||||
`ServiceStack <https://servicestack.net/>`_,"``ServiceStack.*``, ``ServiceStack``",,7,194,
|
||||
System,"``System.*``, ``System``",3,2336,28,5
|
||||
Others,"``Dapper``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Primitives``, ``Microsoft.VisualBasic``, ``MySql.Data.MySqlClient``, ``Newtonsoft.Json``",,149,137,
|
||||
Totals,,3,2492,359,5
|
||||
Others,"``Dapper``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.Extensions.Primitives``, ``Microsoft.VisualBasic``, ``MySql.Data.MySqlClient``, ``Newtonsoft.Json``",,149,131,
|
||||
Totals,,3,2492,353,5
|
||||
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
## 1.1.4
|
||||
|
||||
## 1.1.3
|
||||
|
||||
## 1.1.2
|
||||
|
||||
## 1.1.1
|
||||
|
||||
## 1.1.0
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
## 1.1.2
|
||||
@@ -1 +0,0 @@
|
||||
## 1.1.3
|
||||
@@ -1 +0,0 @@
|
||||
## 1.1.4
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.1.4
|
||||
lastReleaseVersion: 1.1.1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-all
|
||||
version: 1.1.4
|
||||
version: 1.1.2-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
## 1.1.4
|
||||
|
||||
## 1.1.3
|
||||
|
||||
## 1.1.2
|
||||
|
||||
## 1.1.1
|
||||
|
||||
## 1.1.0
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
## 1.1.2
|
||||
@@ -1 +0,0 @@
|
||||
## 1.1.3
|
||||
@@ -1 +0,0 @@
|
||||
## 1.1.4
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.1.4
|
||||
lastReleaseVersion: 1.1.1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-queries
|
||||
version: 1.1.4
|
||||
version: 1.1.2-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
## 0.2.3
|
||||
|
||||
## 0.2.2
|
||||
|
||||
## 0.2.1
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
## 0.2.1
|
||||
@@ -1 +0,0 @@
|
||||
## 0.2.2
|
||||
@@ -1 +0,0 @@
|
||||
## 0.2.3
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.2.3
|
||||
lastReleaseVersion: 0.2.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-all
|
||||
version: 0.2.3
|
||||
version: 0.2.1-dev
|
||||
groups: csharp
|
||||
dbscheme: semmlecode.csharp.dbscheme
|
||||
extractor: csharp
|
||||
|
||||
@@ -491,7 +491,7 @@ module Opcodes {
|
||||
|
||||
override Callable getTarget() { none() }
|
||||
|
||||
/** Gets the function pointer type targeted by this instruction. */
|
||||
/** Gets the function pointer type targetted by this instruction. */
|
||||
FunctionPointerType getTargetType() { cil_access(this, result) }
|
||||
|
||||
// The number of items popped/pushed from the stack depends on the target of
|
||||
|
||||
@@ -117,7 +117,7 @@ class Modifiable extends Declaration, @modifiable {
|
||||
* Note that explicit interface implementations are also considered effectively
|
||||
* `private` if the implemented interface is itself effectively `private`. Finally,
|
||||
* `private protected` members are not considered effectively `private`, because
|
||||
* they can be overridden within the declaring assembly.
|
||||
* they can be overriden within the declaring assembly.
|
||||
*/
|
||||
predicate isEffectivelyPrivate() {
|
||||
this.isReallyPrivate() or
|
||||
@@ -143,7 +143,7 @@ class Modifiable extends Declaration, @modifiable {
|
||||
* considered. Explicit interface implementations are also considered effectively
|
||||
* `internal` if the implemented interface is itself effectively `internal`. Finally,
|
||||
* `internal protected` members are not considered effectively `internal`, because
|
||||
* they can be overridden outside the declaring assembly.
|
||||
* they can be overriden outside the declaring assembly.
|
||||
*/
|
||||
predicate isEffectivelyInternal() {
|
||||
this.isReallyInternal() or
|
||||
|
||||
@@ -265,7 +265,7 @@ class TypeMentionNode extends PrintAstNode, TTypeMentionNode {
|
||||
final TypeMention getTypeMention() { result = typeMention }
|
||||
|
||||
/**
|
||||
* Gets the `Element` targeted by the `TypeMention`.
|
||||
* Gets the `Element` targetted by the `TypeMention`.
|
||||
*/
|
||||
final Element getTarget() { result = typeMention.getTarget() }
|
||||
|
||||
|
||||
@@ -672,7 +672,7 @@ module Unification {
|
||||
* `ConstrainedTypeParameter::unifiable()` can be used.
|
||||
*
|
||||
*
|
||||
* For performance reasons, type parameter constraints inside `t1` and `t2` are
|
||||
* For performance reasons, type paramater constraints inside `t1` and `t2` are
|
||||
* *not* taken into account, and there is also no guarantee that the same type
|
||||
* parameter can be substituted with two different terms. For example, in
|
||||
*
|
||||
|
||||
@@ -744,7 +744,7 @@ cached
|
||||
private module Cached {
|
||||
/**
|
||||
* If needed, call this predicate from `ControlFlowGraphImplSpecific.qll` in order to
|
||||
* force a stage-dependency on the `ControlFlowGraphImplShared.qll` stage and thereby
|
||||
* force a stage-dependency on the `ControlFlowGraphImplShared.qll` stage and therby
|
||||
* collapsing the two stages.
|
||||
*/
|
||||
cached
|
||||
|
||||
@@ -515,7 +515,9 @@ Element interpretElement(
|
||||
/**
|
||||
* Holds if `c` has a `generated` summary.
|
||||
*/
|
||||
predicate hasSummary(Callable c, boolean generated) { summaryElement(c, _, _, _, generated) }
|
||||
predicate hasSummary(DataFlowCallable c, boolean generated) {
|
||||
summaryElement(c, _, _, _, generated)
|
||||
}
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/** Provides classes and predicates for defining flow summaries. */
|
||||
|
||||
import csharp
|
||||
private import dotnet
|
||||
private import internal.FlowSummaryImpl as Impl
|
||||
private import internal.DataFlowDispatch as DataFlowDispatch
|
||||
|
||||
@@ -139,6 +138,28 @@ private class RecordConstructorFlow extends SummarizedCallable {
|
||||
}
|
||||
}
|
||||
|
||||
private class SummarizedCallableDefaultClearsContent extends Impl::Public::SummarizedCallable {
|
||||
SummarizedCallableDefaultClearsContent() {
|
||||
this instanceof Impl::Public::SummarizedCallable or none()
|
||||
}
|
||||
|
||||
// By default, we assume that all stores into arguments are definite
|
||||
override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
|
||||
exists(SummaryComponentStack output, SummaryComponent target |
|
||||
this.propagatesFlow(_, output, _) and
|
||||
output.drop(_) =
|
||||
SummaryComponentStack::push(SummaryComponent::content(content),
|
||||
SummaryComponentStack::singleton(target)) and
|
||||
not content instanceof DataFlow::ElementContent
|
||||
|
|
||||
target = SummaryComponent::argument(pos.getPosition())
|
||||
or
|
||||
target = SummaryComponent::qualifier() and
|
||||
pos.isThisParameter()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack;
|
||||
|
||||
private class RecordConstructorFlowRequiredSummaryComponentStack extends RequiredSummaryComponentStack {
|
||||
|
||||
@@ -1,529 +0,0 @@
|
||||
/**
|
||||
* Provides classes for performing global (inter-procedural)
|
||||
* content-sensitive data flow analyses.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
|
||||
module ContentDataFlow {
|
||||
private import DataFlowImplSpecific::Private
|
||||
private import DataFlowImplSpecific::Private as DataFlowPrivate
|
||||
private import DataFlowImplForContentDataFlow as DF
|
||||
|
||||
class Node = DF::Node;
|
||||
|
||||
class FlowFeature = DF::FlowFeature;
|
||||
|
||||
class ContentSet = DF::ContentSet;
|
||||
|
||||
predicate stageStats = DF::stageStats/8;
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the global data flow library must define its own unique extension
|
||||
* of this abstract class. To create a configuration, extend this class with
|
||||
* a subclass whose characteristic predicate is a unique singleton string.
|
||||
* For example, write
|
||||
*
|
||||
* ```ql
|
||||
* class MyAnalysisConfiguration extends ContentDataFlowConfiguration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isBarrier`.
|
||||
* // Optionally override `isAdditionalFlowStep`.
|
||||
* // Optionally override `getAFeature`.
|
||||
* // Optionally override `accessPathLimit`.
|
||||
* // Optionally override `isRelevantContent`.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Unlike `DataFlow::Configuration` (on which this class is based), we allow
|
||||
* for data to be stored (possibly nested) inside contents of sources and sinks.
|
||||
* We track flow paths of the form
|
||||
*
|
||||
* ```
|
||||
* source --value-->* node
|
||||
* (--read--> node --value-->* node)*
|
||||
* --(non-value|value)-->* node
|
||||
* (--store--> node --value-->* node)*
|
||||
* --value-->* sink
|
||||
* ```
|
||||
*
|
||||
* where `--value-->` is a value-preserving flow step, `--read-->` is a read
|
||||
* step, `--store-->` is a store step, and `--(non-value)-->` is a
|
||||
* non-value-preserving flow step.
|
||||
*
|
||||
* That is, first a sequence of 0 or more reads, followed by 0 or more additional
|
||||
* steps, followed by 0 or more stores, with value-preserving steps allowed in
|
||||
* between all other steps.
|
||||
*/
|
||||
abstract class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
abstract predicate isSource(Node source);
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
abstract predicate isSink(Node sink);
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Gets a limit on the number of reads out of sources and number of stores into sinks. */
|
||||
int accessPathLimit() { result = DataFlowPrivate::accessPathLimit() }
|
||||
|
||||
/** Holds if `c` is relevant for reads out of sources or stores into sinks. */
|
||||
predicate isRelevantContent(ContentSet c) { any() }
|
||||
|
||||
/**
|
||||
* Holds if data stored inside `sourceAp` on `source` flows to `sinkAp` inside `sink`
|
||||
* for this configuration. `preservesValue` indicates whether any of the additional
|
||||
* flow steps defined by `isAdditionalFlowStep` are needed.
|
||||
*
|
||||
* For the source access path, `sourceAp`, the top of the stack represents the content
|
||||
* that was last read from. That is, if `sourceAp` is `Field1.Field2` (with `Field1`
|
||||
* being the top of the stack), then there is flow from `source.Field2.Field1`.
|
||||
*
|
||||
* For the sink access path, `sinkAp`, the top of the stack represents the content
|
||||
* that was last stored into. That is, if `sinkAp` is `Field1.Field2` (with `Field1`
|
||||
* being the top of the stack), then there is flow into `sink.Field1.Field2`.
|
||||
*/
|
||||
final predicate hasFlow(
|
||||
Node source, AccessPath sourceAp, Node sink, AccessPath sinkAp, boolean preservesValue
|
||||
) {
|
||||
exists(DF::PathNode pathSource, DF::PathNode pathSink |
|
||||
this.(ConfigurationAdapter).hasFlowPath(pathSource, pathSink) and
|
||||
nodeReaches(pathSource, TAccessPathNil(), TAccessPathNil(), pathSink, sourceAp, sinkAp) and
|
||||
source = pathSource.getNode() and
|
||||
sink = pathSink.getNode()
|
||||
|
|
||||
pathSink.getState().(InitState).decode(preservesValue)
|
||||
or
|
||||
pathSink.getState().(ReadState).decode(_, preservesValue)
|
||||
or
|
||||
pathSink.getState().(StoreState).decode(_, preservesValue)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A flow state representing no reads or stores. */
|
||||
private class InitState extends DF::FlowState {
|
||||
private boolean preservesValue_;
|
||||
|
||||
InitState() { this = "Init(" + preservesValue_ + ")" and preservesValue_ in [false, true] }
|
||||
|
||||
predicate decode(boolean preservesValue) { preservesValue = preservesValue_ }
|
||||
}
|
||||
|
||||
/** A flow state representing that content has been stored into. */
|
||||
private class StoreState extends DF::FlowState {
|
||||
private boolean preservesValue_;
|
||||
private int size_;
|
||||
|
||||
StoreState() {
|
||||
preservesValue_ in [false, true] and
|
||||
size_ in [1 .. any(Configuration c).accessPathLimit()] and
|
||||
this = "StoreState(" + size_ + "," + preservesValue_ + ")"
|
||||
}
|
||||
|
||||
predicate decode(int size, boolean preservesValue) {
|
||||
size = size_ and preservesValue = preservesValue_
|
||||
}
|
||||
}
|
||||
|
||||
/** A flow state representing that content has been read from. */
|
||||
private class ReadState extends DF::FlowState {
|
||||
private boolean preservesValue_;
|
||||
private int size_;
|
||||
|
||||
ReadState() {
|
||||
preservesValue_ in [false, true] and
|
||||
size_ in [1 .. any(Configuration c).accessPathLimit()] and
|
||||
this = "ReadState(" + size_ + "," + preservesValue_ + ")"
|
||||
}
|
||||
|
||||
predicate decode(int size, boolean preservesValue) {
|
||||
size = size_ and preservesValue = preservesValue_
|
||||
}
|
||||
}
|
||||
|
||||
private predicate storeStep(
|
||||
Node node1, DF::FlowState state1, ContentSet c, Node node2, StoreState state2,
|
||||
Configuration config
|
||||
) {
|
||||
exists(boolean preservesValue, int size |
|
||||
storeSet(node1, c, node2, _, _) and
|
||||
config.isRelevantContent(c) and
|
||||
state2.decode(size + 1, preservesValue)
|
||||
|
|
||||
state1.(InitState).decode(preservesValue) and size = 0
|
||||
or
|
||||
state1.(ReadState).decode(_, preservesValue) and size = 0
|
||||
or
|
||||
state1.(StoreState).decode(size, preservesValue)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate readStep(
|
||||
Node node1, DF::FlowState state1, ContentSet c, Node node2, ReadState state2,
|
||||
Configuration config
|
||||
) {
|
||||
exists(int size |
|
||||
readSet(node1, c, node2) and
|
||||
config.isRelevantContent(c) and
|
||||
state2.decode(size + 1, true)
|
||||
|
|
||||
state1.(InitState).decode(true) and
|
||||
size = 0
|
||||
or
|
||||
state1.(ReadState).decode(size, true)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate additionalStep(
|
||||
Node node1, DF::FlowState state1, Node node2, DF::FlowState state2, Configuration config
|
||||
) {
|
||||
config.isAdditionalFlowStep(node1, node2) and
|
||||
(
|
||||
state1 instanceof InitState and
|
||||
state2.(InitState).decode(false)
|
||||
or
|
||||
exists(int size |
|
||||
state1.(ReadState).decode(size, _) and
|
||||
state2.(ReadState).decode(size, false)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private class ConfigurationAdapter extends DF::Configuration {
|
||||
private Configuration c;
|
||||
|
||||
ConfigurationAdapter() { this = c }
|
||||
|
||||
final override predicate isSource(Node source, DF::FlowState state) {
|
||||
c.isSource(source) and
|
||||
state.(InitState).decode(true)
|
||||
}
|
||||
|
||||
final override predicate isSink(Node sink, DF::FlowState state) {
|
||||
c.isSink(sink) and
|
||||
(
|
||||
state instanceof InitState or
|
||||
state instanceof StoreState or
|
||||
state instanceof ReadState
|
||||
)
|
||||
}
|
||||
|
||||
final override predicate isAdditionalFlowStep(
|
||||
Node node1, DF::FlowState state1, Node node2, DF::FlowState state2
|
||||
) {
|
||||
storeStep(node1, state1, _, node2, state2, this) or
|
||||
readStep(node1, state1, _, node2, state2, this) or
|
||||
additionalStep(node1, state1, node2, state2, this)
|
||||
}
|
||||
|
||||
final override predicate isBarrier(Node node) { c.isBarrier(node) }
|
||||
|
||||
final override FlowFeature getAFeature() { result = c.getAFeature() }
|
||||
|
||||
// needed to record reads/stores inside summarized callables
|
||||
final override predicate includeHiddenNodes() { any() }
|
||||
}
|
||||
|
||||
private newtype TAccessPath =
|
||||
TAccessPathNil() or
|
||||
TAccessPathCons(ContentSet head, AccessPath tail) {
|
||||
nodeReachesStore(_, _, _, _, head, _, tail)
|
||||
or
|
||||
nodeReachesRead(_, _, _, _, head, tail, _)
|
||||
}
|
||||
|
||||
/** An access path. */
|
||||
class AccessPath extends TAccessPath {
|
||||
/** Gets the head of this access path, if any. */
|
||||
ContentSet getHead() { this = TAccessPathCons(result, _) }
|
||||
|
||||
/** Gets the tail of this access path, if any. */
|
||||
AccessPath getTail() { this = TAccessPathCons(_, result) }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this access path.
|
||||
*
|
||||
* Elements are dot-separated, and the head of the stack is
|
||||
* rendered first.
|
||||
*/
|
||||
string toString() {
|
||||
this = TAccessPathNil() and
|
||||
result = ""
|
||||
or
|
||||
exists(ContentSet head, AccessPath tail |
|
||||
this = TAccessPathCons(head, tail) and
|
||||
result = head + "." + tail
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// important to use `edges` and not `PathNode::getASuccessor()`, as the latter
|
||||
// is not pruned for reachability
|
||||
private predicate pathSucc = DF::PathGraph::edges/2;
|
||||
|
||||
/**
|
||||
* Provides a big-step flow relation, where flow stops at read/store steps that
|
||||
* must be recorded, and flow via `subpaths` such that reads/stores inside
|
||||
* summarized callables can be recorded as well.
|
||||
*/
|
||||
private module BigStepFlow {
|
||||
private predicate reachesSink(DF::PathNode node) {
|
||||
any(ConfigurationAdapter config).isSink(node.getNode(), node.getState())
|
||||
or
|
||||
exists(DF::PathNode mid |
|
||||
pathSucc(node, mid) and
|
||||
reachesSink(mid)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the flow step `pred -> succ` should not be allowed to be included
|
||||
* in the big-step relation.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate excludeStep(DF::PathNode pred, DF::PathNode succ) {
|
||||
pathSucc(pred, succ) and
|
||||
(
|
||||
// we need to record reads/stores inside summarized callables
|
||||
DF::PathGraph::subpaths(pred, _, _, succ)
|
||||
or
|
||||
// only allow flow into a summarized callable, as part of the big-step
|
||||
// relation, when flow can reach a sink without going back out
|
||||
DF::PathGraph::subpaths(pred, succ, _, _) and
|
||||
not reachesSink(succ)
|
||||
or
|
||||
// needed to record store steps
|
||||
storeStep(pred.getNode(), pred.getState(), _, succ.getNode(), succ.getState(),
|
||||
pred.getConfiguration())
|
||||
or
|
||||
// needed to record read steps
|
||||
readStep(pred.getNode(), pred.getState(), _, succ.getNode(), succ.getState(),
|
||||
pred.getConfiguration())
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable getEnclosingCallableImpl(DF::PathNode node) {
|
||||
result = getNodeEnclosingCallable(node.getNode())
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private DataFlowCallable getEnclosingCallable(DF::PathNode node) {
|
||||
pragma[only_bind_into](result) = getEnclosingCallableImpl(pragma[only_bind_out](node))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate bigStepEntry(DF::PathNode node) {
|
||||
node.getConfiguration() instanceof Configuration and
|
||||
(
|
||||
any(ConfigurationAdapter config).isSource(node.getNode(), node.getState())
|
||||
or
|
||||
excludeStep(_, node)
|
||||
or
|
||||
DF::PathGraph::subpaths(_, node, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate bigStepExit(DF::PathNode node) {
|
||||
node.getConfiguration() instanceof Configuration and
|
||||
(
|
||||
bigStepEntry(node)
|
||||
or
|
||||
any(ConfigurationAdapter config).isSink(node.getNode(), node.getState())
|
||||
or
|
||||
excludeStep(node, _)
|
||||
or
|
||||
DF::PathGraph::subpaths(_, _, node, _)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate step(DF::PathNode pred, DF::PathNode succ) {
|
||||
pathSucc(pred, succ) and
|
||||
not excludeStep(pred, succ)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate stepRec(DF::PathNode pred, DF::PathNode succ) {
|
||||
step(pred, succ) and
|
||||
not bigStepEntry(pred)
|
||||
}
|
||||
|
||||
private predicate stepRecPlus(DF::PathNode n1, DF::PathNode n2) = fastTC(stepRec/2)(n1, n2)
|
||||
|
||||
/**
|
||||
* Holds if there is flow `pathSucc+(pred) = succ`, and such a flow path does
|
||||
* not go through any reads/stores that need to be recorded, or summarized
|
||||
* steps.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate bigStep(DF::PathNode pred, DF::PathNode succ) {
|
||||
exists(DF::PathNode mid |
|
||||
bigStepEntry(pred) and
|
||||
step(pred, mid)
|
||||
|
|
||||
succ = mid
|
||||
or
|
||||
stepRecPlus(mid, succ)
|
||||
) and
|
||||
bigStepExit(succ)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate bigStepNotLocal(DF::PathNode pred, DF::PathNode succ) {
|
||||
bigStep(pred, succ) and
|
||||
not getEnclosingCallable(pred) = getEnclosingCallable(succ)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate bigStepMaybeLocal(DF::PathNode pred, DF::PathNode succ) {
|
||||
bigStep(pred, succ) and
|
||||
getEnclosingCallable(pred) = getEnclosingCallable(succ)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `source` can reach `node`, having read `reads` from the source and
|
||||
* written `stores` into `node`.
|
||||
*
|
||||
* `source` is either a source from a configuration, in which case `scReads` and
|
||||
* `scStores` are always empty, or it is the parameter of a summarized callable,
|
||||
* in which case `scReads` and `scStores` record the reads/stores for a summary
|
||||
* context, that is, the reads/stores for an argument that can reach the parameter.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nodeReaches(
|
||||
DF::PathNode source, AccessPath scReads, AccessPath scStores, DF::PathNode node,
|
||||
AccessPath reads, AccessPath stores
|
||||
) {
|
||||
exists(ConfigurationAdapter config |
|
||||
node = source and
|
||||
reads = scReads and
|
||||
stores = scStores
|
||||
|
|
||||
config.hasFlowPath(source, _) and
|
||||
scReads = TAccessPathNil() and
|
||||
scStores = TAccessPathNil()
|
||||
or
|
||||
// the argument in a sub path can be reached, so we start flow from the sub path
|
||||
// parameter, while recording the read/store summary context
|
||||
exists(DF::PathNode arg |
|
||||
nodeReachesSubpathArg(_, _, _, arg, scReads, scStores) and
|
||||
DF::PathGraph::subpaths(arg, source, _, _)
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(DF::PathNode mid |
|
||||
nodeReaches(source, scReads, scStores, mid, reads, stores) and
|
||||
BigStepFlow::bigStepMaybeLocal(mid, node)
|
||||
)
|
||||
or
|
||||
exists(DF::PathNode mid |
|
||||
nodeReaches(source, scReads, scStores, mid, reads, stores) and
|
||||
BigStepFlow::bigStepNotLocal(mid, node) and
|
||||
// when flow is not local, we cannot flow back out, so we may stop
|
||||
// flow early when computing summary flow
|
||||
any(ConfigurationAdapter config).hasFlowPath(source, _) and
|
||||
scReads = TAccessPathNil() and
|
||||
scStores = TAccessPathNil()
|
||||
)
|
||||
or
|
||||
// store step
|
||||
exists(AccessPath storesMid, ContentSet c |
|
||||
nodeReachesStore(source, scReads, scStores, node, c, reads, storesMid) and
|
||||
stores = TAccessPathCons(c, storesMid)
|
||||
)
|
||||
or
|
||||
// read step
|
||||
exists(AccessPath readsMid, ContentSet c |
|
||||
nodeReachesRead(source, scReads, scStores, node, c, readsMid, stores) and
|
||||
reads = TAccessPathCons(c, readsMid)
|
||||
)
|
||||
or
|
||||
// flow-through step; match outer stores/reads with inner store/read summary contexts
|
||||
exists(DF::PathNode mid, AccessPath innerScReads, AccessPath innerScStores |
|
||||
nodeReachesSubpathArg(source, scReads, scStores, mid, innerScReads, innerScStores) and
|
||||
subpathArgReachesOut(mid, innerScReads, innerScStores, node, reads, stores)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeReachesStore(
|
||||
DF::PathNode source, AccessPath scReads, AccessPath scStores, DF::PathNode node, ContentSet c,
|
||||
AccessPath reads, AccessPath stores
|
||||
) {
|
||||
exists(DF::PathNode mid |
|
||||
nodeReaches(source, scReads, scStores, mid, reads, stores) and
|
||||
storeStep(mid.getNode(), mid.getState(), c, node.getNode(), node.getState(),
|
||||
node.getConfiguration()) and
|
||||
pathSucc(mid, node)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeReachesRead(
|
||||
DF::PathNode source, AccessPath scReads, AccessPath scStores, DF::PathNode node, ContentSet c,
|
||||
AccessPath reads, AccessPath stores
|
||||
) {
|
||||
exists(DF::PathNode mid |
|
||||
nodeReaches(source, scReads, scStores, mid, reads, stores) and
|
||||
readStep(mid.getNode(), mid.getState(), c, node.getNode(), node.getState(),
|
||||
node.getConfiguration()) and
|
||||
pathSucc(mid, node)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeReachesSubpathArg(
|
||||
DF::PathNode source, AccessPath scReads, AccessPath scStores, DF::PathNode arg,
|
||||
AccessPath reads, AccessPath stores
|
||||
) {
|
||||
nodeReaches(source, scReads, scStores, arg, reads, stores) and
|
||||
DF::PathGraph::subpaths(arg, _, _, _)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate subpathArgReachesOut(
|
||||
DF::PathNode arg, AccessPath scReads, AccessPath scStores, DF::PathNode out, AccessPath reads,
|
||||
AccessPath stores
|
||||
) {
|
||||
exists(DF::PathNode source, DF::PathNode ret |
|
||||
nodeReaches(source, scReads, scStores, ret, reads, stores) and
|
||||
DF::PathGraph::subpaths(arg, source, ret, out)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,14 @@ private import semmle.code.csharp.dispatch.RuntimeCallable
|
||||
private import semmle.code.csharp.frameworks.system.Collections
|
||||
private import semmle.code.csharp.frameworks.system.collections.Generic
|
||||
|
||||
private predicate summarizedCallable(DataFlowCallable c) {
|
||||
c instanceof FlowSummary::SummarizedCallable
|
||||
or
|
||||
FlowSummaryImpl::Private::summaryReturnNode(_, TJumpReturnKind(c, _))
|
||||
or
|
||||
c = interpretElement(_, _, _, _, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a source declaration of callable `c` that has a body or has
|
||||
* a flow summary.
|
||||
@@ -21,6 +29,9 @@ private import semmle.code.csharp.frameworks.system.collections.Generic
|
||||
*/
|
||||
DotNet::Callable getCallableForDataFlow(DotNet::Callable c) {
|
||||
exists(DotNet::Callable unboundDecl | unboundDecl = c.getUnboundDeclaration() |
|
||||
summarizedCallable(unboundDecl) and
|
||||
result = unboundDecl
|
||||
or
|
||||
result.hasBody() and
|
||||
if unboundDecl.getFile().fromSource()
|
||||
then
|
||||
@@ -70,46 +81,17 @@ newtype TReturnKind =
|
||||
v = def.getSourceVariable().getAssignable()
|
||||
)
|
||||
} or
|
||||
TJumpReturnKind(Callable target, ReturnKind rk) {
|
||||
target.isUnboundDeclaration() and
|
||||
TJumpReturnKind(DataFlowCallable target, ReturnKind rk) {
|
||||
rk instanceof NormalReturnKind and
|
||||
(
|
||||
rk instanceof NormalReturnKind and
|
||||
(
|
||||
target instanceof Constructor or
|
||||
not target.getReturnType() instanceof VoidType
|
||||
)
|
||||
or
|
||||
exists(target.getParameter(rk.(OutRefReturnKind).getPosition()))
|
||||
target instanceof Constructor or
|
||||
not target.getReturnType() instanceof VoidType
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A summarized callable where the summary should be used for dataflow analysis.
|
||||
*/
|
||||
class DataFlowSummarizedCallable instanceof FlowSummary::SummarizedCallable {
|
||||
DataFlowSummarizedCallable() {
|
||||
not this.fromSource()
|
||||
or
|
||||
this.fromSource() and not this.isAutoGenerated()
|
||||
exists(target.getParameter(rk.(OutRefReturnKind).getPosition()))
|
||||
}
|
||||
|
||||
string toString() { result = super.toString() }
|
||||
}
|
||||
|
||||
private module Cached {
|
||||
/**
|
||||
* The following heuristic is used to rank when to use source code or when to use summaries for DataFlowCallables.
|
||||
* 1. Use hand written summaries.
|
||||
* 2. Use source code.
|
||||
* 3. Use auto generated summaries.
|
||||
*/
|
||||
cached
|
||||
newtype TDataFlowCallable =
|
||||
TDotNetCallable(DotNet::Callable c) {
|
||||
c.isUnboundDeclaration() and not c instanceof DataFlowSummarizedCallable
|
||||
} or
|
||||
TSummarizedCallable(DataFlowSummarizedCallable sc)
|
||||
|
||||
cached
|
||||
newtype TDataFlowCall =
|
||||
TNonDelegateCall(ControlFlow::Nodes::ElementNode cfn, DispatchCall dc) {
|
||||
@@ -126,7 +108,7 @@ private module Cached {
|
||||
// No need to include calls that are compiled from source
|
||||
not call.getImplementation().getMethod().compiledFromSource()
|
||||
} or
|
||||
TSummaryCall(FlowSummaryImpl::Public::SummarizedCallable c, Node receiver) {
|
||||
TSummaryCall(FlowSummary::SummarizedCallable c, Node receiver) {
|
||||
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
|
||||
}
|
||||
|
||||
@@ -162,7 +144,7 @@ private module DispatchImpl {
|
||||
* call is a delegate call, or if the qualifier accesses a parameter of
|
||||
* the enclosing callable `c` (including the implicit `this` parameter).
|
||||
*/
|
||||
predicate mayBenefitFromCallContext(NonDelegateDataFlowCall call, DataFlowCallable c) {
|
||||
predicate mayBenefitFromCallContext(NonDelegateDataFlowCall call, Callable c) {
|
||||
c = call.getEnclosingCallable() and
|
||||
call.getDispatchCall().mayBenefitFromCallContext()
|
||||
}
|
||||
@@ -172,7 +154,7 @@ private module DispatchImpl {
|
||||
* restricted to those `call`s for which a context might make a difference.
|
||||
*/
|
||||
DataFlowCallable viableImplInCallContext(NonDelegateDataFlowCall call, DataFlowCall ctx) {
|
||||
result.getUnderlyingCallable() =
|
||||
result =
|
||||
call.getDispatchCall()
|
||||
.getADynamicTargetInCallContext(ctx.(NonDelegateDataFlowCall).getDispatchCall())
|
||||
.getUnboundDeclaration()
|
||||
@@ -251,13 +233,13 @@ class ImplicitCapturedReturnKind extends ReturnKind, TImplicitCapturedReturnKind
|
||||
* one API entry point and out of another.
|
||||
*/
|
||||
class JumpReturnKind extends ReturnKind, TJumpReturnKind {
|
||||
private Callable target;
|
||||
private DataFlowCallable target;
|
||||
private ReturnKind rk;
|
||||
|
||||
JumpReturnKind() { this = TJumpReturnKind(target, rk) }
|
||||
|
||||
/** Gets the target of the jump. */
|
||||
Callable getTarget() { result = target }
|
||||
DataFlowCallable getTarget() { result = target }
|
||||
|
||||
/** Gets the return kind of the target. */
|
||||
ReturnKind getTargetReturnKind() { result = rk }
|
||||
@@ -265,24 +247,8 @@ class JumpReturnKind extends ReturnKind, TJumpReturnKind {
|
||||
override string toString() { result = "jump to " + target }
|
||||
}
|
||||
|
||||
/** A callable used for data flow. */
|
||||
class DataFlowCallable extends TDataFlowCallable {
|
||||
/** Get the underlying source code callable, if any. */
|
||||
DotNet::Callable asCallable() { this = TDotNetCallable(result) }
|
||||
|
||||
/** Get the underlying summarized callable, if any. */
|
||||
FlowSummary::SummarizedCallable asSummarizedCallable() { this = TSummarizedCallable(result) }
|
||||
|
||||
/** Get the underlying callable. */
|
||||
DotNet::Callable getUnderlyingCallable() {
|
||||
result = this.asCallable() or result = this.asSummarizedCallable()
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this dataflow callable. */
|
||||
string toString() { result = this.getUnderlyingCallable().toString() }
|
||||
|
||||
/** Get the location of this dataflow callable. */
|
||||
Location getLocation() { result = this.getUnderlyingCallable().getLocation() }
|
||||
class DataFlowCallable extends DotNet::Callable {
|
||||
DataFlowCallable() { this.isUnboundDeclaration() }
|
||||
}
|
||||
|
||||
/** A call relevant for data flow. */
|
||||
@@ -340,32 +306,18 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall {
|
||||
DispatchCall getDispatchCall() { result = dc }
|
||||
|
||||
override DataFlowCallable getARuntimeTarget() {
|
||||
result.asCallable() = getCallableForDataFlow(dc.getADynamicTarget())
|
||||
result = getCallableForDataFlow(dc.getADynamicTarget())
|
||||
or
|
||||
exists(Callable c, boolean static |
|
||||
result.asSummarizedCallable() = c and
|
||||
c = this.getATarget(static)
|
||||
|
|
||||
static = false
|
||||
or
|
||||
static = true and not c instanceof RuntimeCallable
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a static or dynamic target of this call. */
|
||||
Callable getATarget(boolean static) {
|
||||
result = dc.getADynamicTarget().getUnboundDeclaration() and static = false
|
||||
or
|
||||
result = dc.getAStaticTarget().getUnboundDeclaration() and static = true
|
||||
result = dc.getAStaticTarget().getUnboundDeclaration() and
|
||||
summarizedCallable(result) and
|
||||
not result instanceof RuntimeCallable
|
||||
}
|
||||
|
||||
override ControlFlow::Nodes::ElementNode getControlFlowNode() { result = cfn }
|
||||
|
||||
override DataFlow::ExprNode getNode() { result.getControlFlowNode() = cfn }
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() {
|
||||
result.getUnderlyingCallable() = cfn.getEnclosingCallable()
|
||||
}
|
||||
override DataFlowCallable getEnclosingCallable() { result = cfn.getEnclosingCallable() }
|
||||
|
||||
override string toString() { result = cfn.toString() }
|
||||
|
||||
@@ -393,9 +345,7 @@ class ExplicitDelegateLikeDataFlowCall extends DelegateDataFlowCall, TExplicitDe
|
||||
|
||||
override DataFlow::ExprNode getNode() { result.getControlFlowNode() = cfn }
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() {
|
||||
result.getUnderlyingCallable() = cfn.getEnclosingCallable()
|
||||
}
|
||||
override DataFlowCallable getEnclosingCallable() { result = cfn.getEnclosingCallable() }
|
||||
|
||||
override string toString() { result = cfn.toString() }
|
||||
|
||||
@@ -413,15 +363,13 @@ class TransitiveCapturedDataFlowCall extends DataFlowCall, TTransitiveCapturedCa
|
||||
|
||||
TransitiveCapturedDataFlowCall() { this = TTransitiveCapturedCall(cfn, target) }
|
||||
|
||||
override DataFlowCallable getARuntimeTarget() { result.getUnderlyingCallable() = target }
|
||||
override DataFlowCallable getARuntimeTarget() { result = target }
|
||||
|
||||
override ControlFlow::Nodes::ElementNode getControlFlowNode() { result = cfn }
|
||||
|
||||
override DataFlow::ExprNode getNode() { none() }
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() {
|
||||
result.getUnderlyingCallable() = cfn.getEnclosingCallable()
|
||||
}
|
||||
override DataFlowCallable getEnclosingCallable() { result = cfn.getEnclosingCallable() }
|
||||
|
||||
override string toString() { result = "[transitive] " + cfn.toString() }
|
||||
|
||||
@@ -436,16 +384,14 @@ class CilDataFlowCall extends DataFlowCall, TCilCall {
|
||||
|
||||
override DataFlowCallable getARuntimeTarget() {
|
||||
// There is no dispatch library for CIL, so do not consider overrides for now
|
||||
result.getUnderlyingCallable() = getCallableForDataFlow(call.getTarget())
|
||||
result = getCallableForDataFlow(call.getTarget())
|
||||
}
|
||||
|
||||
override ControlFlow::Nodes::ElementNode getControlFlowNode() { none() }
|
||||
|
||||
override DataFlow::ExprNode getNode() { result.getExpr() = call }
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() {
|
||||
result.getUnderlyingCallable() = call.getEnclosingCallable()
|
||||
}
|
||||
override DataFlowCallable getEnclosingCallable() { result = call.getEnclosingCallable() }
|
||||
|
||||
override string toString() { result = call.toString() }
|
||||
|
||||
@@ -460,7 +406,7 @@ class CilDataFlowCall extends DataFlowCall, TCilCall {
|
||||
* the method `Select`.
|
||||
*/
|
||||
class SummaryCall extends DelegateDataFlowCall, TSummaryCall {
|
||||
private FlowSummaryImpl::Public::SummarizedCallable c;
|
||||
private FlowSummary::SummarizedCallable c;
|
||||
private Node receiver;
|
||||
|
||||
SummaryCall() { this = TSummaryCall(c, receiver) }
|
||||
@@ -476,7 +422,7 @@ class SummaryCall extends DelegateDataFlowCall, TSummaryCall {
|
||||
|
||||
override DataFlow::Node getNode() { none() }
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() { result.asSummarizedCallable() = c }
|
||||
override DataFlowCallable getEnclosingCallable() { result = c }
|
||||
|
||||
override string toString() { result = "[summary] call to " + receiver + " in " + c }
|
||||
|
||||
|
||||
@@ -216,9 +216,10 @@ private module LambdaFlow {
|
||||
or
|
||||
// jump step
|
||||
exists(Node mid, DataFlowType t0 |
|
||||
revLambdaFlow(lambdaCall, kind, mid, t0, _, _, lastCall) and
|
||||
revLambdaFlow(lambdaCall, kind, mid, t0, _, _, _) and
|
||||
toReturn = false and
|
||||
toJump = true
|
||||
toJump = true and
|
||||
lastCall = TDataFlowCallNone()
|
||||
|
|
||||
jumpStepCached(node, mid) and
|
||||
t = t0
|
||||
@@ -304,7 +305,7 @@ cached
|
||||
private module Cached {
|
||||
/**
|
||||
* If needed, call this predicate from `DataFlowImplSpecific.qll` in order to
|
||||
* force a stage-dependency on the `DataFlowImplCommon.qll` stage and thereby
|
||||
* force a stage-dependency on the `DataFlowImplCommon.qll` stage and therby
|
||||
* collapsing the two stages.
|
||||
*/
|
||||
cached
|
||||
@@ -788,31 +789,24 @@ private module Cached {
|
||||
cached
|
||||
predicate readSet(Node node1, ContentSet c, Node node2) { readStep(node1, c, node2) }
|
||||
|
||||
cached
|
||||
predicate storeSet(
|
||||
Node node1, ContentSet c, Node node2, DataFlowType contentType, DataFlowType containerType
|
||||
) {
|
||||
storeStep(node1, c, node2) and
|
||||
contentType = getNodeDataFlowType(node1) and
|
||||
containerType = getNodeDataFlowType(node2)
|
||||
or
|
||||
exists(Node n1, Node n2 |
|
||||
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
|
||||
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
||||
|
|
||||
argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1)
|
||||
or
|
||||
readSet(n2, c, n1) and
|
||||
contentType = getNodeDataFlowType(n1) and
|
||||
containerType = getNodeDataFlowType(n2)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate store(
|
||||
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
|
||||
) {
|
||||
exists(ContentSet cs |
|
||||
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
|
||||
exists(ContentSet cs | c = cs.getAStoreContent() |
|
||||
storeStep(node1, cs, node2) and
|
||||
contentType = getNodeDataFlowType(node1) and
|
||||
containerType = getNodeDataFlowType(node2)
|
||||
or
|
||||
exists(Node n1, Node n2 |
|
||||
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
|
||||
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
||||
|
|
||||
argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, cs, contentType), n1)
|
||||
or
|
||||
readSet(n2, cs, n1) and
|
||||
contentType = getNodeDataFlowType(n1) and
|
||||
containerType = getNodeDataFlowType(n2)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user