mirror of
https://github.com/github/codeql.git
synced 2026-04-20 22:44:52 +02:00
Merge branch 'main' into coredata
This commit is contained in:
62
.github/workflows/ql-for-ql-tests.yml
vendored
62
.github/workflows/ql-for-ql-tests.yml
vendored
@@ -33,19 +33,73 @@ jobs:
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
ql/target
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-qltest-cargo-${{ hashFiles('ql/**/Cargo.lock') }}
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-qltest-cargo-${{ hashFiles('ql/rust-toolchain.toml', 'ql/**/Cargo.lock') }}
|
||||
- name: Build extractor
|
||||
run: |
|
||||
cd ql;
|
||||
codeqlpath=$(dirname ${{ steps.find-codeql.outputs.codeql-path }});
|
||||
env "PATH=$PATH:$codeqlpath" ./scripts/create-extractor-pack.sh
|
||||
- name: Cache compilation cache
|
||||
id: query-cache
|
||||
uses: ./.github/actions/cache-query-compilation
|
||||
with:
|
||||
key: ql-for-ql-tests
|
||||
- name: Run QL tests
|
||||
run: |
|
||||
"${CODEQL}" test run --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --search-path "${{ github.workspace }}/ql/extractor-pack" --consistency-queries ql/ql/consistency-queries ql/ql/test
|
||||
"${CODEQL}" test run --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --search-path "${{ github.workspace }}/ql/extractor-pack" --consistency-queries ql/ql/consistency-queries --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" ql/ql/test
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- name: Check QL formatting
|
||||
|
||||
other-os:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest]
|
||||
needs: [qltest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install GNU tar
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
find ql/ql/src "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 "${CODEQL}" query format --check-only
|
||||
brew install gnu-tar
|
||||
echo "/usr/local/opt/gnu-tar/libexec/gnubin" >> $GITHUB_PATH
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- uses: ./.github/actions/os-version
|
||||
id: os_version
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
ql/target
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-qltest-cargo-${{ hashFiles('ql/rust-toolchain.toml', 'ql/**/Cargo.lock') }}
|
||||
- name: Build extractor
|
||||
if: runner.os != 'Windows'
|
||||
run: |
|
||||
cd ql;
|
||||
codeqlpath=$(dirname ${{ steps.find-codeql.outputs.codeql-path }});
|
||||
env "PATH=$PATH:$codeqlpath" ./scripts/create-extractor-pack.sh
|
||||
- name: Build extractor (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
cd ql;
|
||||
$Env:PATH += ";$(dirname ${{ steps.find-codeql.outputs.codeql-path }})"
|
||||
pwsh ./scripts/create-extractor-pack.ps1
|
||||
- name: Run a single QL tests - Unix
|
||||
if: runner.os != 'Windows'
|
||||
run: |
|
||||
"${CODEQL}" test run --check-databases --search-path "${{ github.workspace }}/ql/extractor-pack" ql/ql/test/queries/style/DeadCode/DeadCode.qlref
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- name: Run a single QL tests - Windows
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
$Env:PATH += ";$(dirname ${{ steps.find-codeql.outputs.codeql-path }})"
|
||||
codeql test run --check-databases --search-path "${{ github.workspace }}/ql/extractor-pack" ql/ql/test/queries/style/DeadCode/DeadCode.qlref
|
||||
|
||||
22
.github/workflows/ruby-build.yml
vendored
22
.github/workflows/ruby-build.yml
vendored
@@ -205,11 +205,6 @@ jobs:
|
||||
- name: Fetch CodeQL
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: Shopify/example-ruby-app
|
||||
ref: 67a0decc5eb550f3a9228eda53925c3afd40dfe9
|
||||
|
||||
- name: Download Ruby bundle
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
@@ -218,26 +213,15 @@ jobs:
|
||||
- name: Unzip Ruby bundle
|
||||
shell: bash
|
||||
run: unzip -q -d "${{ runner.temp }}/ruby-bundle" "${{ runner.temp }}/codeql-ruby-bundle.zip"
|
||||
- name: Prepare test files
|
||||
shell: bash
|
||||
run: |
|
||||
echo "import codeql.ruby.AST select count(File f)" > "test.ql"
|
||||
echo "| 4 |" > "test.expected"
|
||||
echo 'name: sample-tests
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
codeql/ruby-all: "*"
|
||||
extractor: ruby
|
||||
tests: .
|
||||
' > qlpack.yml
|
||||
|
||||
- name: Run QL test
|
||||
shell: bash
|
||||
run: |
|
||||
codeql test run --search-path "${{ runner.temp }}/ruby-bundle" --additional-packs "${{ runner.temp }}/ruby-bundle" .
|
||||
codeql test run --search-path "${{ runner.temp }}/ruby-bundle" --additional-packs "${{ runner.temp }}/ruby-bundle" ruby/ql/test/library-tests/ast/constants/
|
||||
- name: Create database
|
||||
shell: bash
|
||||
run: |
|
||||
codeql database create --search-path "${{ runner.temp }}/ruby-bundle" --language ruby --source-root . ../database
|
||||
codeql database create --search-path "${{ runner.temp }}/ruby-bundle" --language ruby --source-root ruby/ql/test/library-tests/ast/constants/ ../database
|
||||
- name: Analyze database
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
@@ -45,6 +45,16 @@ module Consistency {
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodeAtPosition`. */
|
||||
predicate uniqueParameterNodeAtPositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodePosition`. */
|
||||
predicate uniqueParameterNodePositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
private class RelevantNode extends Node {
|
||||
@@ -246,6 +256,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodeAtPosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodeAtPositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and
|
||||
msg = "Parameters with overlapping positions."
|
||||
@@ -254,6 +265,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodePosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodePositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
|
||||
@@ -45,6 +45,16 @@ module Consistency {
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodeAtPosition`. */
|
||||
predicate uniqueParameterNodeAtPositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodePosition`. */
|
||||
predicate uniqueParameterNodePositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
private class RelevantNode extends Node {
|
||||
@@ -246,6 +256,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodeAtPosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodeAtPositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and
|
||||
msg = "Parameters with overlapping positions."
|
||||
@@ -254,6 +265,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodePosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodePositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
|
||||
@@ -45,6 +45,16 @@ module Consistency {
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodeAtPosition`. */
|
||||
predicate uniqueParameterNodeAtPositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodePosition`. */
|
||||
predicate uniqueParameterNodePositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
private class RelevantNode extends Node {
|
||||
@@ -246,6 +256,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodeAtPosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodeAtPositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and
|
||||
msg = "Parameters with overlapping positions."
|
||||
@@ -254,6 +265,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodePosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodePositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
|
||||
@@ -15,76 +15,24 @@
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import TaintedWithPath
|
||||
|
||||
string getATopLevelDomain() {
|
||||
result =
|
||||
[
|
||||
"com", "ru", "net", "org", "de", "jp", "uk", "br", "pl", "in", "it", "fr", "au", "info", "nl",
|
||||
"cn", "ir", "es", "cz", "biz", "ca", "eu", "ua", "kr", "za", "co", "gr", "ro", "se", "tw",
|
||||
"vn", "mx", "ch", "tr", "at", "be", "hu", "tv", "dk", "me", "ar", "us", "no", "sk", "fi",
|
||||
"id", "cl", "nz", "by", "xyz", "pt", "ie", "il", "kz", "my", "hk", "lt", "cc", "sg", "io",
|
||||
"edu", "gov"
|
||||
]
|
||||
}
|
||||
|
||||
predicate hardCodedAddressOrIP(StringLiteral txt) {
|
||||
exists(string s | s = txt.getValueText() |
|
||||
// Hard-coded ip addresses, such as 127.0.0.1
|
||||
s.regexpMatch("\"[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+\"") or
|
||||
// Hard-coded addresses such as www.mycompany.com
|
||||
s.matches("\"www.%\"") or
|
||||
s.matches("\"http:%\"") or
|
||||
s.matches("\"https:%\"") or
|
||||
s.matches("\"%.com\"") or
|
||||
s.matches("\"%.ru\"") or
|
||||
s.matches("\"%.net\"") or
|
||||
s.matches("\"%.org\"") or
|
||||
s.matches("\"%.de\"") or
|
||||
s.matches("\"%.jp\"") or
|
||||
s.matches("\"%.uk\"") or
|
||||
s.matches("\"%.br\"") or
|
||||
s.matches("\"%.pl\"") or
|
||||
s.matches("\"%.in\"") or
|
||||
s.matches("\"%.it\"") or
|
||||
s.matches("\"%.fr\"") or
|
||||
s.matches("\"%.au\"") or
|
||||
s.matches("\"%.info\"") or
|
||||
s.matches("\"%.nl\"") or
|
||||
s.matches("\"%.cn\"") or
|
||||
s.matches("\"%.ir\"") or
|
||||
s.matches("\"%.es\"") or
|
||||
s.matches("\"%.cz\"") or
|
||||
s.matches("\"%.biz\"") or
|
||||
s.matches("\"%.ca\"") or
|
||||
s.matches("\"%.eu\"") or
|
||||
s.matches("\"%.ua\"") or
|
||||
s.matches("\"%.kr\"") or
|
||||
s.matches("\"%.za\"") or
|
||||
s.matches("\"%.co\"") or
|
||||
s.matches("\"%.gr\"") or
|
||||
s.matches("\"%.ro\"") or
|
||||
s.matches("\"%.se\"") or
|
||||
s.matches("\"%.tw\"") or
|
||||
s.matches("\"%.vn\"") or
|
||||
s.matches("\"%.mx\"") or
|
||||
s.matches("\"%.ch\"") or
|
||||
s.matches("\"%.tr\"") or
|
||||
s.matches("\"%.at\"") or
|
||||
s.matches("\"%.be\"") or
|
||||
s.matches("\"%.hu\"") or
|
||||
s.matches("\"%.tv\"") or
|
||||
s.matches("\"%.dk\"") or
|
||||
s.matches("\"%.me\"") or
|
||||
s.matches("\"%.ar\"") or
|
||||
s.matches("\"%.us\"") or
|
||||
s.matches("\"%.no\"") or
|
||||
s.matches("\"%.sk\"") or
|
||||
s.matches("\"%.fi\"") or
|
||||
s.matches("\"%.id\"") or
|
||||
s.matches("\"%.cl\"") or
|
||||
s.matches("\"%.nz\"") or
|
||||
s.matches("\"%.by\"") or
|
||||
s.matches("\"%.xyz\"") or
|
||||
s.matches("\"%.pt\"") or
|
||||
s.matches("\"%.ie\"") or
|
||||
s.matches("\"%.il\"") or
|
||||
s.matches("\"%.kz\"") or
|
||||
s.matches("\"%.my\"") or
|
||||
s.matches("\"%.hk\"") or
|
||||
s.matches("\"%.lt\"") or
|
||||
s.matches("\"%.cc\"") or
|
||||
s.matches("\"%.sg\"") or
|
||||
s.matches("\"%.io\"") or
|
||||
s.matches("\"%.edu\"") or
|
||||
s.matches("\"%.gov\"")
|
||||
s.regexpMatch("\"(www\\.|http:|https:).*\"") or
|
||||
s.regexpMatch("\".*\\.(" + strictconcat(getATopLevelDomain(), "|") + ")\"")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -207,34 +207,35 @@ bad_asts.cpp:
|
||||
# 27| Type = [SpecifiedType] const Point
|
||||
# 27| ValueCategory = lvalue
|
||||
# 28| getStmt(1): [ReturnStmt] return ...
|
||||
# 30| [TopLevelFunction] void Bad::errorExpr()
|
||||
# 30| <params>:
|
||||
# 30| getEntryPoint(): [BlockStmt] { ... }
|
||||
# 31| getStmt(0): [DeclStmt] declaration
|
||||
# 31| getDeclarationEntry(0): [VariableDeclarationEntry] definition of intref
|
||||
# 31| Type = [LValueReferenceType] int &
|
||||
# 31| getVariable().getInitializer(): [Initializer] initializer for intref
|
||||
# 31| getExpr(): [ErrorExpr] <error expr>
|
||||
# 31| Type = [ErroneousType] error
|
||||
# 31| ValueCategory = prvalue
|
||||
# 32| getStmt(1): [DeclStmt] declaration
|
||||
# 32| getDeclarationEntry(0): [VariableDeclarationEntry] definition of x
|
||||
# 32| Type = [IntType] int
|
||||
# 32| getVariable().getInitializer(): [Initializer] initializer for x
|
||||
# 32| getExpr(): [ErrorExpr] <error expr>
|
||||
# 32| Type = [ErroneousType] error
|
||||
# 32| ValueCategory = prvalue
|
||||
# 33| getStmt(2): [ExprStmt] ExprStmt
|
||||
# 33| getExpr(): [AssignExpr] ... = ...
|
||||
# 33| Type = [IntType] int
|
||||
# 33| ValueCategory = lvalue
|
||||
# 33| getLValue(): [VariableAccess] x
|
||||
# 33| Type = [IntType] int
|
||||
# 33| ValueCategory = lvalue
|
||||
# 33| getRValue(): [ErrorExpr] <error expr>
|
||||
# 33| Type = [ErroneousType] error
|
||||
# 33| ValueCategory = prvalue(load)
|
||||
# 34| getStmt(3): [ReturnStmt] return ...
|
||||
bad_stmts.cpp:
|
||||
# 5| [TopLevelFunction] void Bad::errorExpr()
|
||||
# 5| <params>:
|
||||
# 5| getEntryPoint(): [BlockStmt] { ... }
|
||||
# 6| getStmt(0): [DeclStmt] declaration
|
||||
# 6| getDeclarationEntry(0): [VariableDeclarationEntry] definition of intref
|
||||
# 6| Type = [LValueReferenceType] int &
|
||||
# 6| getVariable().getInitializer(): [Initializer] initializer for intref
|
||||
# 6| getExpr(): [ErrorExpr] <error expr>
|
||||
# 6| Type = [ErroneousType] error
|
||||
# 6| ValueCategory = prvalue
|
||||
# 7| getStmt(1): [DeclStmt] declaration
|
||||
# 7| getDeclarationEntry(0): [VariableDeclarationEntry] definition of x
|
||||
# 7| Type = [IntType] int
|
||||
# 7| getVariable().getInitializer(): [Initializer] initializer for x
|
||||
# 7| getExpr(): [ErrorExpr] <error expr>
|
||||
# 7| Type = [ErroneousType] error
|
||||
# 7| ValueCategory = prvalue
|
||||
# 8| getStmt(2): [ExprStmt] ExprStmt
|
||||
# 8| getExpr(): [AssignExpr] ... = ...
|
||||
# 8| Type = [IntType] int
|
||||
# 8| ValueCategory = lvalue
|
||||
# 8| getLValue(): [VariableAccess] x
|
||||
# 8| Type = [IntType] int
|
||||
# 8| ValueCategory = lvalue
|
||||
# 8| getRValue(): [ErrorExpr] <error expr>
|
||||
# 8| Type = [ErroneousType] error
|
||||
# 8| ValueCategory = prvalue(load)
|
||||
# 9| getStmt(3): [ReturnStmt] return ...
|
||||
clang.cpp:
|
||||
# 5| [TopLevelFunction] int* globalIntAddress()
|
||||
# 5| <params>:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// semmle-extractor-options: -std=c++17 --expect_errors
|
||||
// semmle-extractor-options: -std=c++17
|
||||
|
||||
// Test cases that illustrate known bad ASTs that we have to work around in IR generation.
|
||||
namespace Bad {
|
||||
@@ -26,10 +26,4 @@ namespace Bad {
|
||||
void CallCopyConstructor(const Point& a) {
|
||||
Point b = a; // Copy constructor contains literal expressions with no values.
|
||||
}
|
||||
|
||||
void errorExpr() {
|
||||
int &intref = 0;
|
||||
int x = 0[0];
|
||||
x = 1[1];
|
||||
}
|
||||
}
|
||||
|
||||
10
cpp/ql/test/library-tests/ir/ir/bad_stmts.cpp
Normal file
10
cpp/ql/test/library-tests/ir/ir/bad_stmts.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
// semmle-extractor-options: -std=c++17 --expect_errors
|
||||
|
||||
// Test cases that illustrate known bad ASTs that we have to work around in IR generation.
|
||||
namespace Bad {
|
||||
void errorExpr() {
|
||||
int &intref = 0;
|
||||
int x = 0[0];
|
||||
x = 1[1];
|
||||
}
|
||||
}
|
||||
@@ -98,17 +98,17 @@
|
||||
| bad_asts.cpp:27:15:27:15 | StoreValue | r27_6 |
|
||||
| bad_asts.cpp:27:15:27:15 | Unary | r27_3 |
|
||||
| bad_asts.cpp:27:15:27:15 | Unary | r27_4 |
|
||||
| bad_asts.cpp:30:8:30:16 | ChiPartial | partial:m30_3 |
|
||||
| bad_asts.cpp:30:8:30:16 | ChiTotal | total:m30_2 |
|
||||
| bad_asts.cpp:30:8:30:16 | SideEffect | m30_3 |
|
||||
| bad_asts.cpp:31:10:31:15 | Address | &:r31_1 |
|
||||
| bad_asts.cpp:31:18:31:19 | StoreValue | r31_2 |
|
||||
| bad_asts.cpp:32:9:32:9 | Address | &:r32_1 |
|
||||
| bad_asts.cpp:32:12:32:16 | StoreValue | r32_2 |
|
||||
| bad_asts.cpp:33:5:33:5 | Address | &:r33_3 |
|
||||
| bad_asts.cpp:33:5:33:12 | Address | &:r33_1 |
|
||||
| bad_asts.cpp:33:5:33:12 | Load | ~m30_4 |
|
||||
| bad_asts.cpp:33:5:33:12 | StoreValue | r33_2 |
|
||||
| bad_stmts.cpp:5:8:5:16 | ChiPartial | partial:m5_3 |
|
||||
| bad_stmts.cpp:5:8:5:16 | ChiTotal | total:m5_2 |
|
||||
| bad_stmts.cpp:5:8:5:16 | SideEffect | m5_3 |
|
||||
| bad_stmts.cpp:6:10:6:15 | Address | &:r6_1 |
|
||||
| bad_stmts.cpp:6:18:6:19 | StoreValue | r6_2 |
|
||||
| bad_stmts.cpp:7:9:7:9 | Address | &:r7_1 |
|
||||
| bad_stmts.cpp:7:12:7:16 | StoreValue | r7_2 |
|
||||
| bad_stmts.cpp:8:5:8:5 | Address | &:r8_3 |
|
||||
| bad_stmts.cpp:8:5:8:12 | Address | &:r8_1 |
|
||||
| bad_stmts.cpp:8:5:8:12 | Load | ~m5_4 |
|
||||
| bad_stmts.cpp:8:5:8:12 | StoreValue | r8_2 |
|
||||
| clang.cpp:5:6:5:21 | Address | &:r5_5 |
|
||||
| clang.cpp:5:6:5:21 | ChiPartial | partial:m5_3 |
|
||||
| clang.cpp:5:6:5:21 | ChiTotal | total:m5_2 |
|
||||
|
||||
@@ -120,25 +120,26 @@ bad_asts.cpp:
|
||||
# 26| v26_10(void) = AliasedUse : ~m?
|
||||
# 26| v26_11(void) = ExitFunction :
|
||||
|
||||
# 30| void Bad::errorExpr()
|
||||
# 30| Block 0
|
||||
# 30| v30_1(void) = EnterFunction :
|
||||
# 30| mu30_2(unknown) = AliasedDefinition :
|
||||
# 30| mu30_3(unknown) = InitializeNonLocal :
|
||||
# 31| r31_1(glval<int &>) = VariableAddress[intref] :
|
||||
# 31| r31_2(error) = Error :
|
||||
# 31| mu31_3(int &) = Store[intref] : &:r31_1, r31_2
|
||||
# 32| r32_1(glval<int>) = VariableAddress[x] :
|
||||
# 32| r32_2(error) = Error :
|
||||
# 32| mu32_3(int) = Store[x] : &:r32_1, r32_2
|
||||
# 33| r33_1(glval<error>) = Error :
|
||||
# 33| r33_2(error) = Load[?] : &:r33_1, ~m?
|
||||
# 33| r33_3(glval<int>) = VariableAddress[x] :
|
||||
# 33| mu33_4(int) = Store[x] : &:r33_3, r33_2
|
||||
# 34| v34_1(void) = NoOp :
|
||||
# 30| v30_4(void) = ReturnVoid :
|
||||
# 30| v30_5(void) = AliasedUse : ~m?
|
||||
# 30| v30_6(void) = ExitFunction :
|
||||
bad_stmts.cpp:
|
||||
# 5| void Bad::errorExpr()
|
||||
# 5| Block 0
|
||||
# 5| v5_1(void) = EnterFunction :
|
||||
# 5| mu5_2(unknown) = AliasedDefinition :
|
||||
# 5| mu5_3(unknown) = InitializeNonLocal :
|
||||
# 6| r6_1(glval<int &>) = VariableAddress[intref] :
|
||||
# 6| r6_2(error) = Error :
|
||||
# 6| mu6_3(int &) = Store[intref] : &:r6_1, r6_2
|
||||
# 7| r7_1(glval<int>) = VariableAddress[x] :
|
||||
# 7| r7_2(error) = Error :
|
||||
# 7| mu7_3(int) = Store[x] : &:r7_1, r7_2
|
||||
# 8| r8_1(glval<error>) = Error :
|
||||
# 8| r8_2(error) = Load[?] : &:r8_1, ~m?
|
||||
# 8| r8_3(glval<int>) = VariableAddress[x] :
|
||||
# 8| mu8_4(int) = Store[x] : &:r8_3, r8_2
|
||||
# 9| v9_1(void) = NoOp :
|
||||
# 5| v5_4(void) = ReturnVoid :
|
||||
# 5| v5_5(void) = AliasedUse : ~m?
|
||||
# 5| v5_6(void) = ExitFunction :
|
||||
|
||||
clang.cpp:
|
||||
# 5| int* globalIntAddress()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
struct Foo
|
||||
struct Allocators
|
||||
{
|
||||
Foo(int x, int y) : m_x(x), m_y(y) {}
|
||||
~Foo() {m_x = m_y = 0;}
|
||||
Allocators(int x, int y) : m_x(x), m_y(y) {}
|
||||
~Allocators() {m_x = m_y = 0;}
|
||||
|
||||
// NB: In Microsoft mode, size_t is predeclared.
|
||||
static void* operator new(size_t sz, int z, int w) { return nullptr; }
|
||||
@@ -13,7 +13,7 @@ struct Foo
|
||||
|
||||
int main()
|
||||
{
|
||||
auto foo = new(11, 22) Foo(33, 44);
|
||||
auto foo = new(11, 22) Allocators(33, 44);
|
||||
delete foo;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
struct Foo {
|
||||
~Foo();
|
||||
struct ArrayDelete {
|
||||
~ArrayDelete();
|
||||
};
|
||||
|
||||
void f() {
|
||||
delete[] (Foo*)nullptr;
|
||||
delete[] (ArrayDelete*)nullptr;
|
||||
}
|
||||
|
||||
@@ -74,8 +74,8 @@ argHasPostUpdate
|
||||
| ir.cpp:625:5:625:5 | s | ArgumentNode is missing PostUpdateNode. |
|
||||
postWithInFlow
|
||||
| VacuousDestructorCall.cpp:10:22:10:22 | i [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:11:4:13 | m_x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:17:4:19 | m_y [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:18:4:20 | m_x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:24:4:26 | m_y [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| assignexpr.cpp:9:4:9:4 | i [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| builtin.c:34:23:34:31 | staticint [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| builtin.c:39:37:39:45 | carry_out [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
|
||||
@@ -1483,17 +1483,17 @@ postWithInFlow
|
||||
| aggregateinitializer.c:3:6:3:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| aggregateinitializer.c:3:11:3:27 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| aggregateinitializer.c:3:11:3:27 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:3:23:3:28 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:3:31:3:36 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:11:4:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:11:4:13 | m_x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:17:4:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:17:4:19 | m_y [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:3:30:3:35 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:3:38:3:43 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:18:4:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:18:4:20 | m_x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:24:4:26 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:24:4:26 | m_y [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:7:56:7:70 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:16:8:16:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:16:14:16:36 | Call [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:16:14:16:36 | new [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:16:14:16:36 | new [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:16:14:16:43 | Call [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:16:14:16:43 | new [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:16:14:16:43 | new [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:18:1:18:1 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| assignexpr.cpp:9:4:9:4 | i [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| bad_asts.cpp:10:7:10:23 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
|
||||
@@ -17,6 +17,24 @@ edges
|
||||
| test.cpp:38:25:38:42 | (const char *)... | test.cpp:42:14:42:20 | address |
|
||||
| test.cpp:38:25:38:42 | (const char *)... | test.cpp:42:14:42:20 | address |
|
||||
| test.cpp:38:25:38:42 | (const char *)... | test.cpp:42:14:42:20 | address indirection |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:52:14:52:20 | address |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:52:14:52:20 | address |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:52:14:52:20 | address indirection |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:56:14:56:20 | address |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:56:14:56:20 | address |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:56:14:56:20 | address indirection |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:60:14:60:20 | address |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:60:14:60:20 | address |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:60:14:60:20 | address indirection |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:52:14:52:20 | address |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:52:14:52:20 | address |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:52:14:52:20 | address indirection |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:56:14:56:20 | address |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:56:14:56:20 | address |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:56:14:56:20 | address indirection |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:60:14:60:20 | address |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:60:14:60:20 | address |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:60:14:60:20 | address indirection |
|
||||
subpaths
|
||||
nodes
|
||||
| test.cpp:16:25:16:30 | call to getenv | semmle.label | call to getenv |
|
||||
@@ -34,7 +52,21 @@ nodes
|
||||
| test.cpp:42:14:42:20 | address | semmle.label | address |
|
||||
| test.cpp:42:14:42:20 | address | semmle.label | address |
|
||||
| test.cpp:42:14:42:20 | address indirection | semmle.label | address indirection |
|
||||
| test.cpp:49:25:49:30 | call to getenv | semmle.label | call to getenv |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | semmle.label | (const char *)... |
|
||||
| test.cpp:52:14:52:20 | address | semmle.label | address |
|
||||
| test.cpp:52:14:52:20 | address | semmle.label | address |
|
||||
| test.cpp:52:14:52:20 | address indirection | semmle.label | address indirection |
|
||||
| test.cpp:56:14:56:20 | address | semmle.label | address |
|
||||
| test.cpp:56:14:56:20 | address | semmle.label | address |
|
||||
| test.cpp:56:14:56:20 | address indirection | semmle.label | address indirection |
|
||||
| test.cpp:60:14:60:20 | address | semmle.label | address |
|
||||
| test.cpp:60:14:60:20 | address | semmle.label | address |
|
||||
| test.cpp:60:14:60:20 | address indirection | semmle.label | address indirection |
|
||||
#select
|
||||
| test.cpp:20:7:20:12 | call to strcmp | test.cpp:16:25:16:30 | call to getenv | test.cpp:20:14:20:20 | address | Untrusted input $@ might be vulnerable to a spoofing attack. | test.cpp:16:25:16:30 | call to getenv | call to getenv |
|
||||
| test.cpp:31:7:31:12 | call to strcmp | test.cpp:27:25:27:30 | call to getenv | test.cpp:31:14:31:20 | address | Untrusted input $@ might be vulnerable to a spoofing attack. | test.cpp:27:25:27:30 | call to getenv | call to getenv |
|
||||
| test.cpp:42:7:42:12 | call to strcmp | test.cpp:38:25:38:30 | call to getenv | test.cpp:42:14:42:20 | address | Untrusted input $@ might be vulnerable to a spoofing attack. | test.cpp:38:25:38:30 | call to getenv | call to getenv |
|
||||
| test.cpp:52:7:52:12 | call to strcmp | test.cpp:49:25:49:30 | call to getenv | test.cpp:52:14:52:20 | address | Untrusted input $@ might be vulnerable to a spoofing attack. | test.cpp:49:25:49:30 | call to getenv | call to getenv |
|
||||
| test.cpp:56:7:56:12 | call to strcmp | test.cpp:49:25:49:30 | call to getenv | test.cpp:56:14:56:20 | address | Untrusted input $@ might be vulnerable to a spoofing attack. | test.cpp:49:25:49:30 | call to getenv | call to getenv |
|
||||
| test.cpp:60:7:60:12 | call to strcmp | test.cpp:49:25:49:30 | call to getenv | test.cpp:60:14:60:20 | address | Untrusted input $@ might be vulnerable to a spoofing attack. | test.cpp:49:25:49:30 | call to getenv | call to getenv |
|
||||
|
||||
@@ -43,3 +43,27 @@ void processRequest3()
|
||||
isServer = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void processRequest4()
|
||||
{
|
||||
const char *address = getenv("SERVERIP");
|
||||
bool cond = false;
|
||||
|
||||
if (strcmp(address, "127.0.0.1")) { cond = true; } // BAD
|
||||
if (strcmp(address, "127_0_0_1")) { cond = true; } // GOOD (not an IP)
|
||||
if (strcmp(address, "127.0.0")) { cond = true; } // GOOD (not an IP)
|
||||
if (strcmp(address, "127.0.0.0.1")) { cond = true; } // GOOD (not an IP)
|
||||
if (strcmp(address, "http://mycompany")) { cond = true; } // BAD
|
||||
if (strcmp(address, "http_//mycompany")) { cond = true; } // GOOD (not an address)
|
||||
if (strcmp(address, "htt://mycompany")) { cond = true; } // GOOD (not an address)
|
||||
if (strcmp(address, "httpp://mycompany")) { cond = true; } // GOOD (not an address)
|
||||
if (strcmp(address, "mycompany.com")) { cond = true; } // BAD
|
||||
if (strcmp(address, "mycompany_com")) { cond = true; } // GOOD (not an address)
|
||||
if (strcmp(address, "mycompany.c")) { cond = true; } // GOOD (not an address)
|
||||
if (strcmp(address, "mycompany.comm")) { cond = true; } // GOOD (not an address)
|
||||
|
||||
if (cond) {
|
||||
isServer = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Remove operators from the virtualizable type.
|
||||
compatibility: full
|
||||
@@ -92,7 +92,8 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
yield return Tuples.cil_parameter_out(pe);
|
||||
if (p.Attributes.HasFlag(ParameterAttributes.In))
|
||||
yield return Tuples.cil_parameter_in(pe);
|
||||
Attribute.Populate(Context, pe, p.GetCustomAttributes());
|
||||
foreach (var c in Attribute.Populate(Context, pe, p.GetCustomAttributes()))
|
||||
yield return c;
|
||||
}
|
||||
|
||||
yield return Tuples.metadata_handle(this, Context.Assembly, MetadataTokens.GetToken(handle));
|
||||
@@ -205,7 +206,8 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
yield return Tuples.cil_newslot(this);
|
||||
|
||||
// Populate attributes
|
||||
Attribute.Populate(Context, this, md.GetCustomAttributes());
|
||||
foreach (var c in Attribute.Populate(Context, this, md.GetCustomAttributes()))
|
||||
yield return c;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Support for `static virtual` and `static abstract` interface members.
|
||||
* Support for *operators* in interface definitions.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Attributes on methods in CIL are now extracted (Bugfix).
|
||||
@@ -129,7 +129,7 @@ pragma[nomagic]
|
||||
private Virtualizable getACompatibleInterfaceMemberAux(Virtualizable m) {
|
||||
result = getACompatibleInterfaceAccessor(m) or
|
||||
result = getACompatibleInterfaceIndexer(m) or
|
||||
result = getACompatibleInterfaceMethod(m)
|
||||
result = getACompatibleRelevantInterfaceMember(m)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,11 +210,13 @@ private predicate getACompatibleInterfaceIndexerAux(Indexer i, ValueOrRefType t)
|
||||
t = getAPossibleImplementor(i.getDeclaringType())
|
||||
}
|
||||
|
||||
private Method getACompatibleInterfaceMethod0(Method m, int i) {
|
||||
result = getAnInterfaceMethodCandidate(m) and
|
||||
private RelevantInterfaceMember getACompatibleRelevantInterfaceMember0(
|
||||
RelevantInterfaceMember m, int i
|
||||
) {
|
||||
result = getARelevantInterfaceMemberCandidate(m) and
|
||||
i = -1
|
||||
or
|
||||
result = getACompatibleInterfaceMethod0(m, i - 1) and
|
||||
result = getACompatibleRelevantInterfaceMember0(m, i - 1) and
|
||||
exists(Type t1, Type t2 |
|
||||
t1 = getArgumentOrReturnType(m, i) and
|
||||
t2 = getArgumentOrReturnType(result, i)
|
||||
@@ -223,32 +225,47 @@ private Method getACompatibleInterfaceMethod0(Method m, int i) {
|
||||
)
|
||||
}
|
||||
|
||||
private Method getACompatibleInterfaceMethod(Method m) {
|
||||
result = getACompatibleInterfaceMethod0(m, m.getNumberOfParameters())
|
||||
/**
|
||||
* A class of callables relevant for interface member compatibility.
|
||||
*/
|
||||
private class RelevantInterfaceMember extends Callable {
|
||||
RelevantInterfaceMember() {
|
||||
this instanceof Method or
|
||||
this instanceof Operator
|
||||
}
|
||||
|
||||
predicate isPublic() {
|
||||
this.(Method).isPublic() or
|
||||
this.(Operator).isPublic()
|
||||
}
|
||||
}
|
||||
|
||||
private RelevantInterfaceMember getACompatibleRelevantInterfaceMember(RelevantInterfaceMember m) {
|
||||
result = getACompatibleRelevantInterfaceMember0(m, m.getNumberOfParameters())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an interface method that may potentially be implemented by `m`.
|
||||
* Gets an interface method or operator that may potentially be implemented by `m`.
|
||||
*
|
||||
* That is, a method with the same name, same number of parameters, and declared
|
||||
* in a type that is a possible implementor type for the interface type.
|
||||
*/
|
||||
private Method getAnInterfaceMethodCandidate(Method m) {
|
||||
getAPotentialInterfaceMethodAux(result, m.getDeclaringType(), m.getUndecoratedName(),
|
||||
private RelevantInterfaceMember getARelevantInterfaceMemberCandidate(RelevantInterfaceMember m) {
|
||||
getAPotentialRelevantInterfaceMemberAux(result, m.getDeclaringType(), m.getUndecoratedName(),
|
||||
m.getNumberOfParameters()) and
|
||||
m.isPublic()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate getAPotentialInterfaceMethodAux(
|
||||
Method m, ValueOrRefType t, string name, int params
|
||||
private predicate getAPotentialRelevantInterfaceMemberAux(
|
||||
RelevantInterfaceMember m, ValueOrRefType t, string name, int params
|
||||
) {
|
||||
t = getAPossibleImplementor(m.getDeclaringType()) and
|
||||
name = m.getUndecoratedName() and
|
||||
params = m.getNumberOfParameters()
|
||||
}
|
||||
|
||||
private Type getArgumentOrReturnType(Method m, int i) {
|
||||
private Type getArgumentOrReturnType(RelevantInterfaceMember m, int i) {
|
||||
i = 0 and result = m.getReturnType()
|
||||
or
|
||||
result = m.getParameter(i - 1).getType()
|
||||
|
||||
@@ -184,7 +184,7 @@ private class TOverridable = @virtualizable or @callable_accessor;
|
||||
|
||||
/**
|
||||
* A declaration that can be overridden or implemented. That is, a method,
|
||||
* a property, an indexer, an event, or an accessor.
|
||||
* a property, an indexer, an event, an accessor, or an operator.
|
||||
*
|
||||
* Unlike `Virtualizable`, this class includes accessors.
|
||||
*/
|
||||
@@ -360,7 +360,7 @@ class Overridable extends Declaration, TOverridable {
|
||||
|
||||
/**
|
||||
* A member where the `virtual` modifier is valid. That is, a method,
|
||||
* a property, an indexer, or an event.
|
||||
* a property, an indexer, an event, or an operator.
|
||||
*
|
||||
* Equivalently, these are the members that can be defined in an interface.
|
||||
*
|
||||
|
||||
@@ -45,6 +45,16 @@ module Consistency {
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodeAtPosition`. */
|
||||
predicate uniqueParameterNodeAtPositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodePosition`. */
|
||||
predicate uniqueParameterNodePositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
private class RelevantNode extends Node {
|
||||
@@ -246,6 +256,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodeAtPosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodeAtPositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and
|
||||
msg = "Parameters with overlapping positions."
|
||||
@@ -254,6 +265,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodePosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodePositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
|
||||
@@ -670,7 +670,7 @@ compiler_generated(unique int id: @modifiable ref);
|
||||
|
||||
@named_exprorstmt = @goto_stmt | @labeled_stmt | @expr;
|
||||
|
||||
@virtualizable = @method | @property | @indexer | @event;
|
||||
@virtualizable = @method | @property | @indexer | @event | @operator;
|
||||
|
||||
exprorstmt_name(
|
||||
unique int parent_id: @named_exprorstmt ref,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Add operators to the virtualizable type.
|
||||
compatibility: full
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,8 @@ private predicate isOsSpecific(Declaration d) {
|
||||
.matches("%" +
|
||||
[
|
||||
"libobjc", "libproc", "System.Diagnostics.Tracing.XplatEventLogger",
|
||||
"System.Threading.AutoreleasePool"
|
||||
"System.Threading.AutoreleasePool",
|
||||
"System.Diagnostics.Tracing.EventSource.<WriteEventString>"
|
||||
] + "%")
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
public interface INumber<T> where T : INumber<T>
|
||||
{
|
||||
static abstract T operator ++(T other);
|
||||
|
||||
static virtual T operator --(T other) => other;
|
||||
|
||||
static abstract T Add(T left, T right);
|
||||
|
||||
static virtual T Subtract(T left, T right) => left;
|
||||
|
||||
static T Zero() => default(T);
|
||||
}
|
||||
|
||||
public class Complex : INumber<Complex>
|
||||
{
|
||||
public double Real { get; private set; } = 0.0;
|
||||
public double Imaginary { get; private set; } = 0.0;
|
||||
|
||||
public Complex() { }
|
||||
|
||||
public static Complex Zero() => new Complex();
|
||||
|
||||
public static Complex operator ++(Complex other) =>
|
||||
new Complex { Real = other.Real + 1.0, Imaginary = other.Imaginary };
|
||||
|
||||
public static Complex operator --(Complex other) =>
|
||||
new Complex { Real = other.Real - 1.0, Imaginary = other.Imaginary };
|
||||
|
||||
public static Complex Add(Complex left, Complex right) =>
|
||||
new Complex { Real = left.Real + right.Real, Imaginary = left.Imaginary + right.Imaginary };
|
||||
|
||||
public static Complex Subtract(Complex left, Complex right) =>
|
||||
new Complex { Real = left.Real - right.Real, Imaginary = left.Imaginary - right.Imaginary };
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
interfacemembers
|
||||
| INumber<> | StaticInterfaceMembers.cs:3:32:3:33 | ++ | abstract |
|
||||
| INumber<> | StaticInterfaceMembers.cs:3:32:3:33 | ++ | public |
|
||||
| INumber<> | StaticInterfaceMembers.cs:3:32:3:33 | ++ | static |
|
||||
| INumber<> | StaticInterfaceMembers.cs:5:31:5:32 | -- | public |
|
||||
| INumber<> | StaticInterfaceMembers.cs:5:31:5:32 | -- | static |
|
||||
| INumber<> | StaticInterfaceMembers.cs:5:31:5:32 | -- | virtual |
|
||||
| INumber<> | StaticInterfaceMembers.cs:7:23:7:25 | Add | abstract |
|
||||
| INumber<> | StaticInterfaceMembers.cs:7:23:7:25 | Add | public |
|
||||
| INumber<> | StaticInterfaceMembers.cs:7:23:7:25 | Add | static |
|
||||
| INumber<> | StaticInterfaceMembers.cs:9:22:9:29 | Subtract | public |
|
||||
| INumber<> | StaticInterfaceMembers.cs:9:22:9:29 | Subtract | static |
|
||||
| INumber<> | StaticInterfaceMembers.cs:9:22:9:29 | Subtract | virtual |
|
||||
| INumber<> | StaticInterfaceMembers.cs:11:14:11:17 | Zero | public |
|
||||
| INumber<> | StaticInterfaceMembers.cs:11:14:11:17 | Zero | static |
|
||||
implements
|
||||
| StaticInterfaceMembers.cs:23:36:23:37 | ++ | StaticInterfaceMembers.cs:3:32:3:33 | ++ |
|
||||
| StaticInterfaceMembers.cs:26:36:26:37 | -- | StaticInterfaceMembers.cs:5:31:5:32 | -- |
|
||||
| StaticInterfaceMembers.cs:29:27:29:29 | Add | StaticInterfaceMembers.cs:7:23:7:25 | Add |
|
||||
| StaticInterfaceMembers.cs:32:27:32:34 | Subtract | StaticInterfaceMembers.cs:9:22:9:29 | Subtract |
|
||||
@@ -0,0 +1,18 @@
|
||||
import csharp
|
||||
|
||||
query predicate interfacemembers(string interface, Member m, string modifier) {
|
||||
exists(Interface i |
|
||||
i.isUnboundDeclaration() and
|
||||
i.getFile().getStem() = "StaticInterfaceMembers" and
|
||||
i.getName() = interface and
|
||||
m = i.getAMember() and
|
||||
modifier = m.getAModifier().getName()
|
||||
)
|
||||
}
|
||||
|
||||
query predicate implements(Overridable o, Virtualizable v) {
|
||||
v.getFile().getStem() = "StaticInterfaceMembers" and
|
||||
(v.isVirtual() or v.isAbstract()) and
|
||||
v.isStatic() and
|
||||
v.getAnImplementor() = o
|
||||
}
|
||||
@@ -7,6 +7,7 @@ type NUL && "%CODEQL_DIST%\codeql" database index-files ^
|
||||
--include-extension=.xml ^
|
||||
--size-limit 10m ^
|
||||
--language xml ^
|
||||
--working-dir=. ^
|
||||
-- ^
|
||||
"%CODEQL_EXTRACTOR_CSHARP_WIP_DATABASE%" ^
|
||||
>nul 2>&1
|
||||
|
||||
@@ -9,6 +9,7 @@ set -eu
|
||||
--include-extension=.xml \
|
||||
--size-limit 10m \
|
||||
--language xml \
|
||||
--working-dir=. \
|
||||
-- \
|
||||
"$CODEQL_EXTRACTOR_CSHARP_WIP_DATABASE" \
|
||||
> /dev/null 2>&1
|
||||
|
||||
@@ -38,13 +38,12 @@ jakarta.ws.rs.client,1,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,
|
||||
jakarta.ws.rs.container,,9,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,,
|
||||
jakarta.ws.rs.core,2,,149,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,94,55
|
||||
java.beans,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
java.io,37,,40,,15,,,,,,,,,,,,,,,,,,,,,,,,,,,22,,,,,,,,40,
|
||||
java.lang,13,,75,,,,,,,,,,,,8,,,,,,4,,,1,,,,,,,,,,,,,,,,56,19
|
||||
java.math,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
java.io,37,,42,,15,,,,,,,,,,,,,,,,,,,,,,,,,,,22,,,,,,,,41,1
|
||||
java.lang,13,,76,,,,,,,,,,,,8,,,,,,4,,,1,,,,,,,,,,,,,,,,53,23
|
||||
java.net,10,3,7,,,,,,,,,,,,,,,10,,,,,,,,,,,,,,,,,,,,,3,7,
|
||||
java.nio,15,,16,,13,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,16,
|
||||
java.sql,11,,1,,,,,,,,4,,,,,,,,,,,,,,,,,7,,,,,,,,,,,,1,
|
||||
java.util,44,,461,,,,,,,,,,,,34,,,,,,,5,2,,1,2,,,,,,,,,,,,,,36,425
|
||||
java.sql,11,,2,,,,,,,,4,,,,,,,,,,,,,,,,,7,,,,,,,,,,,,1,1
|
||||
java.util,44,,465,,,,,,,,,,,,34,,,,,,,5,2,,1,2,,,,,,,,,,,,,,38,427
|
||||
javax.faces.context,2,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,7,,
|
||||
javax.jms,,9,57,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,57,
|
||||
javax.json,,,123,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,100,23
|
||||
|
||||
|
@@ -18,10 +18,10 @@ Java framework & library support
|
||||
`Google Guava <https://guava.dev/>`_,``com.google.common.*``,,728,39,,6,,,,,
|
||||
JBoss Logging,``org.jboss.logging``,,,324,,,,,,,
|
||||
`JSON-java <https://github.com/stleary/JSON-java>`_,``org.json``,,236,,,,,,,,
|
||||
Java Standard Library,``java.*``,3,602,130,28,,,7,,,10
|
||||
Java Standard Library,``java.*``,3,609,130,28,,,7,,,10
|
||||
Java extensions,"``javax.*``, ``jakarta.*``",63,609,32,,,4,,1,1,2
|
||||
Kotlin Standard Library,``kotlin*``,,1835,12,10,,,,,,2
|
||||
`Spring <https://spring.io/>`_,``org.springframework.*``,29,477,101,,,,19,14,,29
|
||||
Others,"``cn.hutool.core.codec``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.hubspot.jinjava``, ``com.mitchellbosecke.pebble``, ``com.opensymphony.xwork2.ognl``, ``com.rabbitmq.client``, ``com.unboundid.ldap.sdk``, ``com.zaxxer.hikari``, ``flexjson``, ``freemarker.cache``, ``freemarker.template``, ``groovy.lang``, ``groovy.util``, ``jodd.json``, ``net.sf.saxon.s9api``, ``ognl``, ``okhttp3``, ``org.apache.commons.codec``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.logging``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.ibatis.jdbc``, ``org.apache.log4j``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.apache.velocity.app``, ``org.apache.velocity.runtime``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.hibernate``, ``org.jdbi.v3.core``, ``org.jooq``, ``org.mvel2``, ``org.scijava.log``, ``org.slf4j``, ``org.thymeleaf``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``retrofit2``",60,300,269,,,,14,18,,3
|
||||
Totals,,217,8449,1563,129,6,10,107,33,1,86
|
||||
Totals,,217,8456,1563,129,6,10,107,33,1,86
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added `AllowContentAccessMethod` to represent the `setAllowContentAccess` method of the `android.webkit.WebSettings` class.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added more dataflow models for frequently-used JDK APIs.
|
||||
* Removed summary model for `java.lang.String#endsWith(String)` and added neutral model for this API.
|
||||
* Added additional taint step for `java.lang.String#endsWith(String)` to `ConditionalBypassFlowConfig`.
|
||||
@@ -63,6 +63,7 @@ extensions:
|
||||
- ["java.io", "File", True, "getAbsolutePath", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.io", "File", True, "getCanonicalFile", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.io", "File", True, "getCanonicalPath", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.io", "File", True, "getName", "()", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.io", "File", True, "toPath", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.io", "File", True, "toString", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.io", "File", True, "toURI", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
@@ -74,6 +75,7 @@ extensions:
|
||||
- ["java.io", "InputStream", True, "readNBytes", "(int)", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.io", "InputStream", True, "transferTo", "(OutputStream)", "", "Argument[-1]", "Argument[0]", "taint", "manual"]
|
||||
- ["java.io", "InputStreamReader", False, "InputStreamReader", "", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["java.io", "IOException", False, "IOException", "(String)", "", "Argument[0]", "Argument[-1].SyntheticField[java.lang.Throwable.message]", "value", "manual"]
|
||||
- ["java.io", "ObjectInput", True, "read", "", "", "Argument[-1]", "Argument[0]", "taint", "manual"]
|
||||
- ["java.io", "ObjectInputStream", False, "ObjectInputStream", "", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["java.io", "OutputStream", True, "write", "(byte[])", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
@@ -84,3 +86,9 @@ extensions:
|
||||
- ["java.io", "StringReader", False, "StringReader", "", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["java.io", "Writer", True, "toString", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.io", "Writer", True, "write", "", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: neutralModel
|
||||
data:
|
||||
- ["java.io", "File", "exists", "()", "manual"]
|
||||
|
||||
@@ -37,9 +37,10 @@ extensions:
|
||||
- ["java.lang", "CharSequence", True, "charAt", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "CharSequence", True, "subSequence", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "CharSequence", True, "toString", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "Exception", False, "Exception", "(String)", "", "Argument[0]", "Argument[-1].SyntheticField[java.lang.Throwable.message]", "value", "manual"]
|
||||
- ["java.lang", "IllegalArgumentException", False, "IllegalArgumentException", "(String)", "", "Argument[0]", "Argument[-1].SyntheticField[java.lang.Throwable.message]", "value", "manual"]
|
||||
- ["java.lang", "IllegalStateException", False, "IllegalStateException", "(String)", "", "Argument[0]", "Argument[-1].SyntheticField[java.lang.Throwable.message]", "value", "manual"]
|
||||
- ["java.lang", "Integer", False, "parseInt", "(String)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "IndexOutOfBoundsException", False, "IndexOutOfBoundsException", "(String)", "", "Argument[0]", "Argument[-1].SyntheticField[java.lang.Throwable.message]", "value", "manual"]
|
||||
- ["java.lang", "Iterable", True, "forEach", "(Consumer)", "", "Argument[-1].Element", "Argument[0].Parameter[0]", "value", "manual"]
|
||||
- ["java.lang", "Iterable", True, "iterator", "()", "", "Argument[-1].Element", "ReturnValue.Element", "value", "manual"]
|
||||
- ["java.lang", "Iterable", True, "spliterator", "()", "", "Argument[-1].Element", "ReturnValue.Element", "value", "manual"]
|
||||
@@ -47,12 +48,13 @@ extensions:
|
||||
- ["java.lang", "Object", True, "clone", "", "", "Argument[-1].MapKey", "ReturnValue.MapKey", "value", "manual"]
|
||||
- ["java.lang", "Object", True, "clone", "", "", "Argument[-1].MapValue", "ReturnValue.MapValue", "value", "manual"]
|
||||
- ["java.lang", "RuntimeException", False, "RuntimeException", "(String)", "", "Argument[0]", "Argument[-1].SyntheticField[java.lang.Throwable.message]", "value", "manual"]
|
||||
- ["java.lang", "RuntimeException", False, "RuntimeException", "(String,Throwable)", "", "Argument[0]", "Argument[-1].SyntheticField[java.lang.Throwable.message]", "value", "manual"]
|
||||
- ["java.lang", "RuntimeException", False, "RuntimeException", "(String,Throwable)", "", "Argument[1]", "Argument[-1].SyntheticField[java.lang.Throwable.cause]", "value", "manual"]
|
||||
- ["java.lang", "RuntimeException", False, "RuntimeException", "(Throwable)", "", "Argument[0]", "Argument[-1].SyntheticField[java.lang.Throwable.cause]", "value", "manual"]
|
||||
- ["java.lang", "String", False, "String", "", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["java.lang", "String", False, "concat", "(String)", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "String", False, "concat", "(String)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "String", False, "copyValueOf", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "String", False, "endsWith", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "String", False, "format", "(Locale,String,Object[])", "", "Argument[1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "String", False, "format", "(Locale,String,Object[])", "", "Argument[2].ArrayElement", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "String", False, "format", "(String,Object[])", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
@@ -87,34 +89,56 @@ extensions:
|
||||
- ["java.lang", "String", False, "valueOf", "(char)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "String", False, "valueOf", "(char[])", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "String", False, "valueOf", "(char[],int,int)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "String", False, "valueOf", "(int)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "StringBuffer", True, "StringBuffer", "(CharSequence)", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["java.lang", "StringBuffer", True, "StringBuffer", "(String)", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["java.lang", "StringBuilder", True, "StringBuilder", "", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["java.lang", "System", False, "arraycopy", "", "", "Argument[0]", "Argument[2]", "taint", "manual"]
|
||||
- ["java.lang", "Throwable", False, "Throwable", "(Throwable)", "", "Argument[0]", "Argument[-1].SyntheticField[java.lang.Throwable.cause]", "value", "manual"]
|
||||
- ["java.lang", "Throwable", False, "getCause", "()", "", "Argument[-1].SyntheticField[java.lang.Throwable.cause]", "ReturnValue", "value", "manual"]
|
||||
- ["java.lang", "Throwable", False, "getMessage", "()", "", "Argument[-1].SyntheticField[java.lang.Throwable.message]", "ReturnValue", "value", "manual"]
|
||||
- ["java.lang", "Throwable", True, "getCause", "()", "", "Argument[-1].SyntheticField[java.lang.Throwable.cause]", "ReturnValue", "value", "manual"]
|
||||
- ["java.lang", "Throwable", True, "getMessage", "()", "", "Argument[-1].SyntheticField[java.lang.Throwable.message]", "ReturnValue", "value", "manual"]
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: neutralModel
|
||||
data:
|
||||
- ["java.lang", "AbstractStringBuilder", "length", "()", "manual"]
|
||||
- ["java.lang", "Boolean", "equals", "(Object)", "manual"]
|
||||
- ["java.lang", "Class", "getClassLoader", "()", "manual"]
|
||||
- ["java.lang", "Class", "getName", "()", "manual"]
|
||||
- ["java.lang", "Class", "getSimpleName", "()", "manual"]
|
||||
- ["java.lang", "Class", "isAssignableFrom", "(Class)", "manual"]
|
||||
- ["java.lang", "Enum", "Enum", "(String,int)", "manual"]
|
||||
- ["java.lang", "Enum", "equals", "(Object)", "manual"]
|
||||
- ["java.lang", "Enum", "name", "()", "manual"]
|
||||
- ["java.lang", "Enum", "toString", "()", "manual"]
|
||||
- ["java.lang", "Long", "equals", "(Object)", "manual"]
|
||||
- ["java.lang", "Object", "equals", "(Object)", "manual"]
|
||||
- ["java.lang", "Object", "getClass", "()", "manual"]
|
||||
- ["java.lang", "Object", "hashCode", "()", "manual"]
|
||||
- ["java.lang", "Object", "toString", "()", "manual"]
|
||||
- ["java.lang", "String", "contains", "(CharSequence)", "manual"]
|
||||
- ["java.lang", "String", "endsWith", "(String)", "manual"]
|
||||
- ["java.lang", "String", "equals", "(Object)", "manual"]
|
||||
- ["java.lang", "String", "equalsIgnoreCase", "(String)", "manual"]
|
||||
- ["java.lang", "String", "hashCode", "()", "manual"]
|
||||
- ["java.lang", "String", "indexOf", "(String)", "manual"]
|
||||
- ["java.lang", "String", "isEmpty", "()", "manual"]
|
||||
- ["java.lang", "String", "length", "()", "manual"]
|
||||
- ["java.lang", "String", "startsWith", "(String)", "manual"]
|
||||
- ["java.lang", "System", "currentTimeMillis", "()", "manual"]
|
||||
- ["java.lang", "System", "nanoTime", "()", "manual"]
|
||||
- ["java.lang", "Thread", "currentThread", "()", "manual"]
|
||||
- ["java.lang", "Thread", "sleep", "(long)", "manual"]
|
||||
|
||||
# The below APIs have numeric flow and are currently being stored as neutral models.
|
||||
# These may be changed to summary models with kinds "value-numeric" and "taint-numeric" (or similar) in the future.
|
||||
- ["java.lang", "Integer", "intValue", "()", "manual"] # taint-numeric
|
||||
- ["java.lang", "Integer", "parseInt", "(String)", "manual"] # taint-numeric
|
||||
- ["java.lang", "Integer", "toString", "(int)", "manual"] # taint-numeric
|
||||
- ["java.lang", "Integer", "valueOf", "(int)", "manual"] # taint-numeric
|
||||
- ["java.lang", "Long", "longValue", "()", "manual"] # taint-numeric
|
||||
- ["java.lang", "Long", "parseLong", "(String)", "manual"] # taint-numeric
|
||||
- ["java.lang", "Long", "toString", "()", "manual"] # taint-numeric
|
||||
- ["java.lang", "Math", "min", "(int,int)", "manual"] # value-numeric
|
||||
- ["java.lang", "String", "valueOf", "(int)", "manual"] # taint-numeric
|
||||
- ["java.lang", "String", "valueOf", "(long)", "manual"] # taint-numeric
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: summaryModel
|
||||
extensible: neutralModel
|
||||
data:
|
||||
- ["java.math", "BigDecimal", False, "BigDecimal", "(String)", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["java.math", "BigDecimal", "compareTo", "(BigDecimal)", "manual"]
|
||||
|
||||
# The below APIs have numeric flow and are currently being stored as neutral models.
|
||||
# These may be changed to summary models with kinds "value-numeric" and "taint-numeric" (or similar) in the future.
|
||||
- ["java.math", "BigDecimal", "BigDecimal", "(String)", "manual"] # taint-numeric
|
||||
- ["java.math", "BigDecimal", "valueOf", "(double)", "manual"] # taint-numeric
|
||||
- ["java.math", "BigDecimal", "valueOf", "(long)", "manual"] # taint-numeric
|
||||
|
||||
@@ -19,4 +19,16 @@ extensions:
|
||||
pack: codeql/java-all
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["java.sql", "PreparedStatement", True, "setString", "(int,String)", "", "Argument[1]", "Argument[-1]", "value", "manual"]
|
||||
- ["java.sql", "ResultSet", True, "getString", "(String)", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: neutralModel
|
||||
data:
|
||||
- ["java.sql", "ResultSet", "next", "()", "manual"]
|
||||
|
||||
# The below APIs have numeric flow and are currently being stored as neutral models.
|
||||
# These may be changed to summary models with kinds "value-numeric" and "taint-numeric" (or similar) in the future.
|
||||
- ["java.sql", "PreparedStatement", "setInt", "(int,int)", "manual"] # value-numeric
|
||||
- ["java.sql", "ResultSet", "getInt", "(String)", "manual"] # taint-numeric
|
||||
|
||||
9
java/ql/lib/ext/java.text.model.yml
Normal file
9
java/ql/lib/ext/java.text.model.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: neutralModel
|
||||
data:
|
||||
# The below APIs have numeric flow and are currently being stored as neutral models.
|
||||
# These may be changed to summary models with kinds "value-numeric" and "taint-numeric" (or similar) in the future.
|
||||
- ["java.text", "DateFormat", "format", "(Date)", "manual"] # taint-numeric
|
||||
- ["java.text", "SimpleDateFormat", "SimpleDateFormat", "(String)", "manual"] # taint-numeric
|
||||
11
java/ql/lib/ext/java.time.model.yml
Normal file
11
java/ql/lib/ext/java.time.model.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: neutralModel
|
||||
data:
|
||||
- ["java.time", "Instant", "now", "()", "manual"]
|
||||
- ["java.time", "ZonedDateTime", "now", "()", "manual"]
|
||||
|
||||
# The below APIs have numeric flow and are currently being stored as neutral models.
|
||||
# These may be changed to summary models with kinds "value-numeric" and "taint-numeric" (or similar) in the future.
|
||||
- ["java.time", "LocalDate", "of", "(int,int,int)", "manual"] # taint-numeric
|
||||
16
java/ql/lib/ext/java.util.concurrent.atomic.model.yml
Normal file
16
java/ql/lib/ext/java.util.concurrent.atomic.model.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["java.util.concurrent.atomic", "AtomicReference", False, "AtomicReference", "(Object)", "", "Argument[0]", "Argument[-1].SyntheticField[java.util.concurrent.atomic.AtomicReference.value]", "value", "manual"]
|
||||
- ["java.util.concurrent.atomic", "AtomicReference", False, "get", "()", "", "Argument[-1].SyntheticField[java.util.concurrent.atomic.AtomicReference.value]", "ReturnValue", "value", "manual"]
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: neutralModel
|
||||
data:
|
||||
# The below APIs have numeric flow and are currently being stored as neutral models.
|
||||
# These may be changed to summary models with kinds "value-numeric" and "taint-numeric" (or similar) in the future.
|
||||
- ["java.util.concurrent.atomic", "AtomicInteger", "AtomicInteger", "(int)", "manual"] # value-numeric
|
||||
- ["java.util.concurrent.atomic", "AtomicInteger", "get", "()", "manual"] # value-numeric
|
||||
@@ -21,3 +21,14 @@ extensions:
|
||||
- ["java.util.concurrent", "TransferQueue", True, "transfer", "(Object)", "", "Argument[0]", "Argument[-1].Element", "value", "manual"]
|
||||
- ["java.util.concurrent", "TransferQueue", True, "tryTransfer", "(Object)", "", "Argument[0]", "Argument[-1].Element", "value", "manual"]
|
||||
- ["java.util.concurrent", "TransferQueue", True, "tryTransfer", "(Object,long,TimeUnit)", "", "Argument[0]", "Argument[-1].Element", "value", "manual"]
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: neutralModel
|
||||
data:
|
||||
- ["java.util.concurrent", "CountDownLatch", "countDown", "()", "manual"]
|
||||
|
||||
# The below APIs have numeric flow and are currently being stored as neutral models.
|
||||
# These may be changed to summary models with kinds "value-numeric" and "taint-numeric" (or similar) in the future.
|
||||
- ["java.util.concurrent", "CountDownLatch", "CountDownLatch", "(int)", "manual"] # value-numeric
|
||||
- ["java.util.concurrent", "CountDownLatch", "getCount", "()", "manual"] # value-numeric
|
||||
|
||||
@@ -335,6 +335,8 @@ extensions:
|
||||
- ["java.util", "Stack", True, "peek", "()", "", "Argument[-1].Element", "ReturnValue", "value", "manual"]
|
||||
- ["java.util", "Stack", True, "pop", "()", "", "Argument[-1].Element", "ReturnValue", "value", "manual"]
|
||||
- ["java.util", "Stack", True, "push", "(Object)", "", "Argument[0]", "Argument[-1].Element", "value", "manual"]
|
||||
- ["java.util", "StringJoiner", False, "add", "(CharSequence)", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.util", "StringJoiner", False, "add", "(CharSequence)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.util", "StringTokenizer", False, "StringTokenizer", "", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["java.util", "StringTokenizer", False, "nextElement", "()", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.util", "StringTokenizer", False, "nextToken", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
@@ -360,8 +362,13 @@ extensions:
|
||||
pack: codeql/java-all
|
||||
extensible: neutralModel
|
||||
data:
|
||||
- ["java.util", "Collections", "emptyList", "()", "manual"]
|
||||
- ["java.util", "ArrayList", "ArrayList", "(int)", "manual"]
|
||||
- ["java.util", "ArrayList", "size", "()", "manual"]
|
||||
- ["java.util", "Collection", "isEmpty", "()", "manual"]
|
||||
- ["java.util", "Collection", "size", "()", "manual"]
|
||||
- ["java.util", "Collections", "emptyList", "()", "manual"]
|
||||
- ["java.util", "Collections", "emptyMap", "()", "manual"]
|
||||
- ["java.util", "Collections", "emptySet", "()", "manual"]
|
||||
- ["java.util", "Iterator", "hasNext", "()", "manual"]
|
||||
- ["java.util", "List", "contains", "(Object)", "manual"]
|
||||
- ["java.util", "List", "isEmpty", "()", "manual"]
|
||||
@@ -371,6 +378,7 @@ extensions:
|
||||
- ["java.util", "Map", "size", "()", "manual"]
|
||||
- ["java.util", "Objects", "equals", "(Object,Object)", "manual"]
|
||||
- ["java.util", "Objects", "hash", "(Object[])", "manual"]
|
||||
- ["java.util", "Objects", "nonNull", "(Object)", "manual"]
|
||||
- ["java.util", "Optional", "empty", "()", "manual"]
|
||||
- ["java.util", "Optional", "isPresent", "()", "manual"]
|
||||
- ["java.util", "Set", "contains", "(Object)", "manual"]
|
||||
@@ -378,3 +386,13 @@ extensions:
|
||||
- ["java.util", "Set", "size", "()", "manual"]
|
||||
- ["java.util", "UUID", "randomUUID", "()", "manual"]
|
||||
- ["java.util", "UUID", "toString", "()", "manual"]
|
||||
|
||||
# The below APIs are currently being stored as neutral models since `WithoutElement` has not yet been implemented for Java.
|
||||
# When `WithoutElement` is implemented, these should be changed to summary models of the form `Argument[-1].WithoutElement -> Argument[-1]`.
|
||||
- ["java.util", "List", "clear", "()", "manual"]
|
||||
- ["java.util", "Map", "clear", "()", "manual"]
|
||||
|
||||
# The below APIs have numeric flow and are currently being stored as neutral models.
|
||||
# These may be changed to summary models with kinds "value-numeric" and "taint-numeric" (or similar) in the future.
|
||||
- ["java.util", "Date", "Date", "(long)", "manual"] # taint-numeric
|
||||
- ["java.util", "Date", "getTime", "()", "manual"] # taint-numeric
|
||||
|
||||
@@ -93,3 +93,4 @@ extensions:
|
||||
extensible: neutralModel
|
||||
data:
|
||||
- ["java.util.stream", "Collectors", "toList", "()", "manual"]
|
||||
- ["java.util.stream", "Collectors", "toSet", "()", "manual"]
|
||||
|
||||
@@ -45,6 +45,16 @@ module Consistency {
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodeAtPosition`. */
|
||||
predicate uniqueParameterNodeAtPositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodePosition`. */
|
||||
predicate uniqueParameterNodePositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
private class RelevantNode extends Node {
|
||||
@@ -246,6 +256,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodeAtPosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodeAtPositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and
|
||||
msg = "Parameters with overlapping positions."
|
||||
@@ -254,6 +265,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodePosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodePositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
|
||||
@@ -78,6 +78,14 @@ class WebViewSetWebViewClientMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/** The method `setAllowContentAccess` of the class `android.webkit.WebSettings` */
|
||||
class AllowContentAccessMethod extends Method {
|
||||
AllowContentAccessMethod() {
|
||||
this.getDeclaringType() instanceof TypeWebSettings and
|
||||
this.hasName("setAllowContentAccess")
|
||||
}
|
||||
}
|
||||
|
||||
/** The method `shouldOverrideUrlLoading` of the class `android.webkit.WebViewClient`. */
|
||||
class ShouldOverrideUrlLoading extends Method {
|
||||
ShouldOverrideUrlLoading() {
|
||||
|
||||
@@ -23,6 +23,19 @@ predicate conditionControlsMethod(MethodAccess ma, Expr e) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node1` to `node2` is a dataflow step through the
|
||||
* `endsWith` method of the `java.lang.String` class.
|
||||
*/
|
||||
private predicate endsWithStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod().getDeclaringType() instanceof TypeString and
|
||||
ma.getMethod().getName() = "endsWith" and
|
||||
ma.getQualifier() = node1.asExpr() and
|
||||
ma = node2.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint tracking configuration for untrusted data flowing to sensitive conditions.
|
||||
*/
|
||||
@@ -32,4 +45,8 @@ class ConditionalBypassFlowConfig extends TaintTracking::Configuration {
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { conditionControlsMethod(_, sink.asExpr()) }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
endsWithStep(node1, node2)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
public class PartialPathTraversalGood {
|
||||
public void example(File dir, File parent) throws IOException {
|
||||
if (!dir.getCanonicalPath().toPath().startsWith(parent.getCanonicalPath().toPath())) {
|
||||
if (!dir.getCanonicalPath().startsWith(parent.getCanonicalPath() + File.separator)) {
|
||||
throw new IOException("Invalid directory: " + dir.getCanonicalPath());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ and not just children of <code>parent</code>, which is a security issue.
|
||||
<p>
|
||||
|
||||
In this example, the <code>if</code> statement checks if <code>parent.getCanonicalPath() + File.separator </code>
|
||||
is a prefix of <code>dir.getCanonicalPath()</code>. Because <code>parent.getCanonicalPath().toPath()</code> is
|
||||
is a prefix of <code>dir.getCanonicalPath()</code>. Because <code>parent.getCanonicalPath() + File.separator</code> is
|
||||
indeed slash-terminated, the user supplying <code>dir</code> can only access children of
|
||||
<code>parent</code>, as desired.
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Android can provide access to content providers within a WebView using
|
||||
the <code>setAllowContentAccess</code> setting.</p>
|
||||
|
||||
<p>Allowing access to content providers via <code>content://</code> URLs
|
||||
may allow JavaScript to access protected content.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
If your app does not require access to the <code>content://</code> URL
|
||||
functionality, you should explicitly disable the setting by
|
||||
calling <code>setAllowContentAccess(false)</code> on the settings of the
|
||||
WebView.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>In the following (bad) example, access to <code>content://</code> URLs is explicitly allowed.</p>
|
||||
|
||||
<sample src="ContentAccessEnabled.java"/>
|
||||
|
||||
<p>In the following (good) example, access to <code>content://</code> URLs is explicitly denied.</p>
|
||||
|
||||
<sample src="ContentAccessDisabled.java"/>
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
Android Documentation: <a href="https://developer.android.com/reference/android/webkit/WebSettings#setAllowContentAccess(boolean)">setAllowContentAccess</a>.
|
||||
</li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* @name Android WebView settings allows access to content links
|
||||
* @id java/android/websettings-allow-content-access
|
||||
* @description Access to content providers in a WebView can allow access to protected information by loading content:// links.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @security-severity 6.5
|
||||
* @tags security
|
||||
* external/cwe/cwe-200
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.frameworks.android.WebView
|
||||
|
||||
/** Represents `android.webkit.WebView` and its subclasses. */
|
||||
private class TypeWebViewOrSubclass extends RefType {
|
||||
TypeWebViewOrSubclass() { this.getASupertype*() instanceof TypeWebView }
|
||||
}
|
||||
|
||||
/**
|
||||
* A method access to a getter method which is private.
|
||||
*
|
||||
* In Kotlin, member accesses are translated to getter methods.
|
||||
*/
|
||||
private class PrivateGetterMethodAccess extends MethodAccess {
|
||||
PrivateGetterMethodAccess() {
|
||||
this.getMethod() instanceof GetterMethod and
|
||||
this.getMethod().isPrivate()
|
||||
}
|
||||
}
|
||||
|
||||
/** A source for `android.webkit.WebView` objects. */
|
||||
class WebViewSource extends DataFlow::Node {
|
||||
WebViewSource() {
|
||||
this.getType() instanceof TypeWebViewOrSubclass and
|
||||
// To reduce duplicate results, we only consider WebView objects from
|
||||
// constructor and method calls, or method accesses which are cast to WebView.
|
||||
(
|
||||
this.asExpr() instanceof ClassInstanceExpr or
|
||||
this.asExpr() instanceof MethodAccess or
|
||||
this.asExpr().(CastExpr).getAChildExpr() instanceof MethodAccess
|
||||
) and
|
||||
// Avoid duplicate results from Kotlin member accesses.
|
||||
not this.asExpr() instanceof PrivateGetterMethodAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A sink representing a call to `android.webkit.WebSettings.setAllowContentAccess` that
|
||||
* disables content access.
|
||||
*/
|
||||
class WebSettingsDisallowContentAccessSink extends DataFlow::Node {
|
||||
WebSettingsDisallowContentAccessSink() {
|
||||
exists(MethodAccess ma |
|
||||
ma.getQualifier() = this.asExpr() and
|
||||
ma.getMethod() instanceof AllowContentAccessMethod and
|
||||
ma.getArgument(0).(CompileTimeConstantExpr).getBooleanValue() = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class WebViewDisallowContentAccessConfiguration extends TaintTracking::Configuration {
|
||||
WebViewDisallowContentAccessConfiguration() { this = "WebViewDisallowContentAccessConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) { node instanceof WebViewSource }
|
||||
|
||||
/**
|
||||
* Holds if the step from `node1` to `node2` is a dataflow step that gets the `WebSettings` object
|
||||
* from the `getSettings` method of a `WebView` object.
|
||||
*
|
||||
* This step is only valid when `state1` is empty and `state2` indicates that the `WebSettings` object
|
||||
* has been accessed.
|
||||
*/
|
||||
override predicate isAdditionalTaintStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
state1 instanceof DataFlow::FlowStateEmpty and
|
||||
state2 = "WebSettings" and
|
||||
// settings = webView.getSettings()
|
||||
// ^node2 = ^node1
|
||||
exists(MethodAccess ma |
|
||||
ma = node2.asExpr() and
|
||||
ma.getQualifier() = node1.asExpr() and
|
||||
ma.getMethod() instanceof WebViewGetSettingsMethod
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
state = "WebSettings" and
|
||||
node instanceof WebSettingsDisallowContentAccessSink
|
||||
}
|
||||
}
|
||||
|
||||
from Expr e
|
||||
where
|
||||
// explicit: setAllowContentAccess(true)
|
||||
exists(MethodAccess ma |
|
||||
ma = e and
|
||||
ma.getMethod() instanceof AllowContentAccessMethod and
|
||||
ma.getArgument(0).(CompileTimeConstantExpr).getBooleanValue() = true
|
||||
)
|
||||
or
|
||||
// implicit: no setAllowContentAccess(false)
|
||||
exists(WebViewSource source |
|
||||
source.asExpr() = e and
|
||||
not any(WebViewDisallowContentAccessConfiguration cfg).hasFlow(source, _)
|
||||
)
|
||||
select e,
|
||||
"Sensitive information may be exposed via a malicious link due to access to content:// links being allowed in this WebView."
|
||||
@@ -0,0 +1,3 @@
|
||||
WebSettings settings = webview.getSettings();
|
||||
|
||||
settings.setAllowContentAccess(false);
|
||||
@@ -0,0 +1,3 @@
|
||||
WebSettings settings = webview.getSettings();
|
||||
|
||||
settings.setAllowContentAccess(true);
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
* Added a new query `java/android/websettings-allow-content-access` to detect Android WebViews which do not disable access to `content://` urls.
|
||||
@@ -12,14 +12,8 @@ edges
|
||||
| ThreadResourceAbuse.java:71:15:71:17 | parameter this [waitTime] : Number | ThreadResourceAbuse.java:74:18:74:25 | this <.field> [waitTime] : Number |
|
||||
| ThreadResourceAbuse.java:74:18:74:25 | this <.field> [waitTime] : Number | ThreadResourceAbuse.java:74:18:74:25 | waitTime |
|
||||
| ThreadResourceAbuse.java:141:27:141:43 | getValue(...) : String | ThreadResourceAbuse.java:144:34:144:42 | delayTime |
|
||||
| ThreadResourceAbuse.java:172:19:172:50 | getHeader(...) : String | ThreadResourceAbuse.java:173:37:173:42 | header : String |
|
||||
| ThreadResourceAbuse.java:172:19:172:50 | getHeader(...) : String | ThreadResourceAbuse.java:176:17:176:26 | retryAfter |
|
||||
| ThreadResourceAbuse.java:173:20:173:43 | parseInt(...) : Number | ThreadResourceAbuse.java:176:17:176:26 | retryAfter |
|
||||
| ThreadResourceAbuse.java:173:37:173:42 | header : String | ThreadResourceAbuse.java:173:20:173:43 | parseInt(...) : Number |
|
||||
| ThreadResourceAbuse.java:206:28:206:56 | getParameter(...) : String | ThreadResourceAbuse.java:207:39:207:52 | uploadDelayStr : String |
|
||||
| ThreadResourceAbuse.java:206:28:206:56 | getParameter(...) : String | ThreadResourceAbuse.java:209:49:209:59 | uploadDelay : Number |
|
||||
| ThreadResourceAbuse.java:207:22:207:53 | parseInt(...) : Number | ThreadResourceAbuse.java:209:49:209:59 | uploadDelay : Number |
|
||||
| ThreadResourceAbuse.java:207:39:207:52 | uploadDelayStr : String | ThreadResourceAbuse.java:207:22:207:53 | parseInt(...) : Number |
|
||||
| ThreadResourceAbuse.java:209:30:209:87 | new UploadListener(...) [slowUploads] : Number | UploadListener.java:28:14:28:19 | parameter this [slowUploads] : Number |
|
||||
| ThreadResourceAbuse.java:209:49:209:59 | uploadDelay : Number | ThreadResourceAbuse.java:209:30:209:87 | new UploadListener(...) [slowUploads] : Number |
|
||||
| ThreadResourceAbuse.java:209:49:209:59 | uploadDelay : Number | UploadListener.java:15:24:15:44 | sleepMilliseconds : Number |
|
||||
@@ -48,12 +42,8 @@ nodes
|
||||
| ThreadResourceAbuse.java:141:27:141:43 | getValue(...) : String | semmle.label | getValue(...) : String |
|
||||
| ThreadResourceAbuse.java:144:34:144:42 | delayTime | semmle.label | delayTime |
|
||||
| ThreadResourceAbuse.java:172:19:172:50 | getHeader(...) : String | semmle.label | getHeader(...) : String |
|
||||
| ThreadResourceAbuse.java:173:20:173:43 | parseInt(...) : Number | semmle.label | parseInt(...) : Number |
|
||||
| ThreadResourceAbuse.java:173:37:173:42 | header : String | semmle.label | header : String |
|
||||
| ThreadResourceAbuse.java:176:17:176:26 | retryAfter | semmle.label | retryAfter |
|
||||
| ThreadResourceAbuse.java:206:28:206:56 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| ThreadResourceAbuse.java:207:22:207:53 | parseInt(...) : Number | semmle.label | parseInt(...) : Number |
|
||||
| ThreadResourceAbuse.java:207:39:207:52 | uploadDelayStr : String | semmle.label | uploadDelayStr : String |
|
||||
| ThreadResourceAbuse.java:209:30:209:87 | new UploadListener(...) [slowUploads] : Number | semmle.label | new UploadListener(...) [slowUploads] : Number |
|
||||
| ThreadResourceAbuse.java:209:49:209:59 | uploadDelay : Number | semmle.label | uploadDelay : Number |
|
||||
| UploadListener.java:15:24:15:44 | sleepMilliseconds : Number | semmle.label | sleepMilliseconds : Number |
|
||||
|
||||
@@ -3,12 +3,8 @@ edges
|
||||
| NFEAndroidDoS.java:13:24:13:61 | getStringExtra(...) : Object | NFEAndroidDoS.java:14:21:14:51 | parseDouble(...) |
|
||||
| NFEAndroidDoS.java:22:21:22:31 | getIntent(...) : Intent | NFEAndroidDoS.java:22:21:22:55 | getStringExtra(...) : Object |
|
||||
| NFEAndroidDoS.java:22:21:22:55 | getStringExtra(...) : Object | NFEAndroidDoS.java:23:15:23:40 | parseInt(...) |
|
||||
| NFEAndroidDoS.java:22:21:22:55 | getStringExtra(...) : Object | NFEAndroidDoS.java:23:32:23:39 | widthStr : Object |
|
||||
| NFEAndroidDoS.java:23:32:23:39 | widthStr : Object | NFEAndroidDoS.java:23:15:23:40 | parseInt(...) |
|
||||
| NFEAndroidDoS.java:25:22:25:32 | getIntent(...) : Intent | NFEAndroidDoS.java:25:22:25:57 | getStringExtra(...) : Object |
|
||||
| NFEAndroidDoS.java:25:22:25:57 | getStringExtra(...) : Object | NFEAndroidDoS.java:26:16:26:42 | parseInt(...) |
|
||||
| NFEAndroidDoS.java:25:22:25:57 | getStringExtra(...) : Object | NFEAndroidDoS.java:26:33:26:41 | heightStr : Object |
|
||||
| NFEAndroidDoS.java:26:33:26:41 | heightStr : Object | NFEAndroidDoS.java:26:16:26:42 | parseInt(...) |
|
||||
| NFEAndroidDoS.java:43:24:43:34 | getIntent(...) : Intent | NFEAndroidDoS.java:43:24:43:61 | getStringExtra(...) : Object |
|
||||
| NFEAndroidDoS.java:43:24:43:61 | getStringExtra(...) : Object | NFEAndroidDoS.java:44:21:44:43 | new Double(...) |
|
||||
| NFEAndroidDoS.java:43:24:43:61 | getStringExtra(...) : Object | NFEAndroidDoS.java:47:21:47:47 | valueOf(...) |
|
||||
@@ -19,11 +15,9 @@ nodes
|
||||
| NFEAndroidDoS.java:22:21:22:31 | getIntent(...) : Intent | semmle.label | getIntent(...) : Intent |
|
||||
| NFEAndroidDoS.java:22:21:22:55 | getStringExtra(...) : Object | semmle.label | getStringExtra(...) : Object |
|
||||
| NFEAndroidDoS.java:23:15:23:40 | parseInt(...) | semmle.label | parseInt(...) |
|
||||
| NFEAndroidDoS.java:23:32:23:39 | widthStr : Object | semmle.label | widthStr : Object |
|
||||
| NFEAndroidDoS.java:25:22:25:32 | getIntent(...) : Intent | semmle.label | getIntent(...) : Intent |
|
||||
| NFEAndroidDoS.java:25:22:25:57 | getStringExtra(...) : Object | semmle.label | getStringExtra(...) : Object |
|
||||
| NFEAndroidDoS.java:26:16:26:42 | parseInt(...) | semmle.label | parseInt(...) |
|
||||
| NFEAndroidDoS.java:26:33:26:41 | heightStr : Object | semmle.label | heightStr : Object |
|
||||
| NFEAndroidDoS.java:43:24:43:34 | getIntent(...) : Intent | semmle.label | getIntent(...) : Intent |
|
||||
| NFEAndroidDoS.java:43:24:43:61 | getStringExtra(...) : Object | semmle.label | getStringExtra(...) : Object |
|
||||
| NFEAndroidDoS.java:44:21:44:43 | new Double(...) | semmle.label | new Double(...) |
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Test {
|
||||
|
||||
@@ -9,37 +19,67 @@ public class Test {
|
||||
|
||||
public void test() throws Exception {
|
||||
|
||||
Exception e1 = new RuntimeException((String)source());
|
||||
sink((String)e1.getMessage()); // $hasValueFlow
|
||||
// top 100 JDK APIs tests
|
||||
{
|
||||
Exception e1 = new RuntimeException((String)source());
|
||||
sink((String)e1.getMessage()); // $hasValueFlow
|
||||
|
||||
Exception e2 = new RuntimeException((Throwable)source());
|
||||
sink((Throwable)e2.getCause()); // $hasValueFlow
|
||||
Exception e2 = new RuntimeException((Throwable)source());
|
||||
sink((Throwable)e2.getCause()); // $hasValueFlow
|
||||
|
||||
Exception e3 = new IllegalArgumentException((String)source());
|
||||
sink((String)e3.getMessage()); // $hasValueFlow
|
||||
Exception e3 = new IllegalArgumentException((String)source());
|
||||
sink((String)e3.getMessage()); // $hasValueFlow
|
||||
|
||||
Exception e4 = new IllegalStateException((String)source());
|
||||
sink((String)e4.getMessage()); // $hasValueFlow
|
||||
Exception e4 = new IllegalStateException((String)source());
|
||||
sink((String)e4.getMessage()); // $hasValueFlow
|
||||
|
||||
Throwable t = new Throwable((Throwable)source());
|
||||
sink((Throwable)t.getCause()); // $hasValueFlow
|
||||
Throwable t = new Throwable((Throwable)source());
|
||||
sink((Throwable)t.getCause()); // $hasValueFlow
|
||||
|
||||
Integer x = (Integer)source();
|
||||
int y = x;
|
||||
sink(String.valueOf(y)); // $hasTaintFlow
|
||||
String s2 = (String)source();
|
||||
int i = 0;
|
||||
sink(s2.charAt(i)); // $hasTaintFlow
|
||||
|
||||
String s1 = (String)source();
|
||||
sink(Integer.parseInt(s1)); // $hasTaintFlow
|
||||
ResultSet rs = (ResultSet)source();
|
||||
sink(rs.getString("")); // $hasTaintFlow
|
||||
}
|
||||
|
||||
String s2 = (String)source();
|
||||
int i = 0;
|
||||
sink(s2.charAt(i)); // $hasTaintFlow
|
||||
// top 200 JDK APIs tests
|
||||
{
|
||||
// java.io
|
||||
Exception e1 = new IOException((String)source());
|
||||
sink((String)e1.getMessage()); // $hasValueFlow
|
||||
|
||||
String s3 = (String)source();
|
||||
sink(new BigDecimal(s3)); // $hasTaintFlow
|
||||
File f = (File)source();
|
||||
sink(f.getName()); // $hasTaintFlow
|
||||
|
||||
ResultSet rs = (ResultSet)source();
|
||||
sink(rs.getString("")); // $hasTaintFlow
|
||||
// java.lang
|
||||
Exception e2 = new Exception((String)source());
|
||||
sink((String)e2.getMessage()); // $hasValueFlow
|
||||
|
||||
Exception e3 = new IndexOutOfBoundsException((String)source());
|
||||
sink((String)e3.getMessage()); // $hasValueFlow
|
||||
|
||||
Exception e4 = new RuntimeException((String)source(), (Throwable)source());
|
||||
sink((String)e4.getMessage()); // $hasValueFlow
|
||||
sink((Throwable)e4.getCause()); // $hasValueFlow
|
||||
|
||||
// java.sql
|
||||
Connection con = DriverManager.getConnection("");
|
||||
PreparedStatement ps1 = con.prepareStatement("UPDATE EMPLOYEES SET NAME = ? WHERE ID = ?");
|
||||
ps1.setString(1, (String)source());
|
||||
sink(ps1); // $hasValueFlow
|
||||
|
||||
// java.util.concurrent.atomic
|
||||
AtomicReference ar = new AtomicReference(source());
|
||||
sink(ar.get()); // $hasValueFlow
|
||||
|
||||
// java.util
|
||||
StringJoiner sj1 = new StringJoiner(",");
|
||||
sink(sj1.add((CharSequence)source())); // $hasTaintFlow
|
||||
|
||||
StringJoiner sj2 = (StringJoiner)source();
|
||||
sink(sj2.add("test")); // $hasTaintFlow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,61 @@ predicate topJdkApiName(string apiName) {
|
||||
"java.nio.file.Path#resolve(String)", "java.lang.Enum#toString()",
|
||||
"java.lang.RuntimeException#RuntimeException(Throwable)", "java.util.Collection#size()",
|
||||
"java.lang.String#charAt(int)", "java.util.stream.Stream#forEach(Consumer)",
|
||||
"java.util.Map#isEmpty()", "java.lang.String#valueOf(int)"
|
||||
"java.util.Map#isEmpty()", "java.lang.String#valueOf(int)",
|
||||
// top 200 JDK APIs
|
||||
"java.lang.Integer#intValue()", "java.util.ArrayList#size()",
|
||||
"java.util.ArrayList#ArrayList(int)", "java.util.function.Function#apply(Object)",
|
||||
"java.util.stream.Stream#forEach(Consumer)", "java.util.ArrayList#get(int)",
|
||||
"java.util.Set#iterator()", "java.util.stream.Collectors#toSet()",
|
||||
"java.lang.String#replaceAll(String,String)", "java.lang.String#getBytes(Charset)",
|
||||
"java.util.Objects#requireNonNull(Object)", "java.util.Objects#nonNull(Object)",
|
||||
"java.lang.String#endsWith(String)", "java.lang.AbstractStringBuilder#length()",
|
||||
"java.sql.PreparedStatement#setString(int,String)",
|
||||
"java.util.regex.Pattern#matcher(CharSequence)", "java.nio.file.Path#toString()",
|
||||
"java.time.Instant#now()", "java.io.File#getAbsolutePath()",
|
||||
"java.util.Set#addAll(Collection)", "java.lang.Integer#valueOf(int)",
|
||||
"java.util.HashSet#HashSet(Collection)", "java.lang.Integer#toString(int)",
|
||||
"java.lang.StringBuilder#StringBuilder(String)", "java.lang.Thread#sleep(long)",
|
||||
"java.lang.Thread#currentThread()", "java.util.Date#getTime()",
|
||||
"java.io.Writer#write(String)", "java.lang.String#getBytes()", "java.io.File#exists()",
|
||||
"java.lang.String#toUpperCase()", "java.lang.Long#parseLong(String)",
|
||||
"java.util.Collections#emptyMap()", "java.util.Optional#orElseThrow(Supplier)",
|
||||
"java.util.List#of(Object,Object)", "java.util.concurrent.CountDownLatch#countDown()",
|
||||
"java.lang.Class#isAssignableFrom(Class)",
|
||||
"java.lang.IndexOutOfBoundsException#IndexOutOfBoundsException(String)",
|
||||
"java.lang.Throwable#getCause()", "java.util.Arrays#stream(Object[])",
|
||||
"java.util.function.Supplier#get()", "java.lang.Exception#Exception(String)",
|
||||
"java.util.function.Consumer#accept(Object)", "java.util.stream.Stream#anyMatch(Predicate)",
|
||||
"java.util.List#clear()", "java.io.File#File(File,String)",
|
||||
"java.lang.String#indexOf(String)", "java.util.List#iterator()",
|
||||
"java.util.concurrent.CountDownLatch#CountDownLatch(int)", "java.sql.ResultSet#next()",
|
||||
"java.sql.PreparedStatement#setInt(int,int)",
|
||||
"java.util.concurrent.atomic.AtomicInteger#get()",
|
||||
"java.util.stream.Collectors#toMap(Function,Function)", "java.lang.Math#min(int,int)",
|
||||
"java.lang.Long#equals(Object)", "java.util.Properties#setProperty(String,String)",
|
||||
"java.util.Map#getOrDefault(Object,Object)", "java.lang.System#getProperty(String)",
|
||||
"java.util.stream.Stream#of(Object[])", "java.nio.file.Paths#get(String,String[])",
|
||||
"java.math.BigDecimal#compareTo(BigDecimal)", "java.math.BigDecimal#valueOf(long)",
|
||||
"java.lang.RuntimeException#RuntimeException(String,Throwable)",
|
||||
"java.util.Collection#add(Object)", "java.util.Collections#emptySet()",
|
||||
"java.util.stream.Stream#flatMap(Function)",
|
||||
"java.util.concurrent.atomic.AtomicReference#get()", "java.util.Collection#isEmpty()",
|
||||
"java.lang.StringBuffer#toString()", "java.util.Collections#singleton(Object)",
|
||||
"java.io.File#getName()", "java.time.ZonedDateTime#now()",
|
||||
"java.io.ByteArrayInputStream#ByteArrayInputStream(byte[])", "java.nio.file.Path#toFile()",
|
||||
"java.util.Date#Date(long)", "java.lang.System#nanoTime()",
|
||||
"java.util.Hashtable#put(Object,Object)", "java.util.Map#putAll(Map)",
|
||||
"java.lang.Long#toString()", "java.util.List#toArray(Object[])", "java.io.File#toPath()",
|
||||
"java.util.regex.Matcher#group(int)", "java.time.LocalDate#of(int,int,int)",
|
||||
"java.lang.String#valueOf(long)", "java.math.BigDecimal#valueOf(double)",
|
||||
"java.io.IOException#IOException(String)", "java.text.DateFormat#format(Date)",
|
||||
"java.sql.ResultSet#getInt(String)", "java.util.Map#clear()", "java.util.HashSet#add(Object)",
|
||||
"java.lang.Class#getClassLoader()", "java.lang.Boolean#equals(Object)",
|
||||
"java.lang.String#concat(String)", "java.util.Collections#singletonMap(Object,Object)",
|
||||
"java.util.Collection#iterator()", "java.util.Map#computeIfAbsent(Object,Function)",
|
||||
"java.text.SimpleDateFormat#SimpleDateFormat(String)",
|
||||
"java.util.StringJoiner#add(CharSequence)", "java.lang.Long#longValue()",
|
||||
"java.util.stream.Collectors#joining(CharSequence)"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -88,10 +142,16 @@ class TopJdkApi extends SummarizedCallableBase {
|
||||
/** Holds if this API has a manual MaD model. */
|
||||
predicate hasManualMadModel() { this.hasManualSummary() or this.hasManualNeutral() }
|
||||
/*
|
||||
* Note: the following top-100 APIs are not modeled with MaD:
|
||||
* java.util.stream.Stream#collect(Collector) : handled separately on a case-by-case basis as it is too complex for MaD
|
||||
* java.lang.String#valueOf(Object) : also a complex case; an alias for `Object.toString`, except the dispatch is hidden
|
||||
* java.lang.Throwable#printStackTrace() : should probably not be a general step, but there might be specialised queries that care
|
||||
* Note: the following top JDK APIs are not modeled with MaD:
|
||||
* `java.lang.String#valueOf(Object)`: a complex case; an alias for `Object.toString`, except the dispatch is hidden
|
||||
* `java.lang.System#getProperty(String)`: needs to be modeled by regular CodeQL matching the get and set keys to reduce FPs
|
||||
* `java.lang.Throwable#printStackTrace()`: should probably not be a general step, but there might be specialised queries that care
|
||||
* `java.util.function.Consumer#accept(Object)`: specialized lambda flow
|
||||
* `java.util.function.Function#apply(Object)`: specialized lambda flow
|
||||
* `java.util.function.Supplier#get()`: lambda flow
|
||||
* `java.util.stream.Collectors#joining(CharSequence)`: cannot be modeled completely without a model for `java.util.stream.Stream#collect(Collector)` as well
|
||||
* `java.util.stream.Collectors#toMap(Function,Function)`: specialized collectors flow
|
||||
* `java.util.stream.Stream#collect(Collector)`: handled separately on a case-by-case basis as it is too complex for MaD
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
| java.lang.String#valueOf(Object) | no manual model |
|
||||
| java.lang.System#getProperty(String) | no manual model |
|
||||
| java.lang.Throwable#printStackTrace() | no manual model |
|
||||
| java.util.function.Consumer#accept(Object) | no manual model |
|
||||
| java.util.function.Function#apply(Object) | no manual model |
|
||||
| java.util.function.Supplier#get() | no manual model |
|
||||
| java.util.stream.Collectors#joining(CharSequence) | no manual model |
|
||||
| java.util.stream.Collectors#toMap(Function,Function) | no manual model |
|
||||
| java.util.stream.Stream#collect(Collector) | no manual model |
|
||||
|
||||
@@ -1,14 +1,27 @@
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.lang.IllegalStateException;
|
||||
import java.lang.IndexOutOfBoundsException;
|
||||
import java.lang.Math;
|
||||
import java.lang.System;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.file.Paths;
|
||||
import java.sql.ResultSet;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.lang.System;
|
||||
import java.lang.IllegalStateException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class TopJdkApisTest { }
|
||||
|
||||
@@ -58,19 +58,19 @@ public class B {
|
||||
// non-whitelisted constructors don't pass taint
|
||||
StringWrapper herring = new StringWrapper(complex);
|
||||
sink(herring);
|
||||
// toString does not pass taint yet
|
||||
// toString does not pass taint yet
|
||||
String valueOfObject = String.valueOf(args);
|
||||
sink(valueOfObject);
|
||||
|
||||
|
||||
|
||||
// tainted equality check with constant
|
||||
boolean cond = "foo" == s;
|
||||
sink(cond);
|
||||
// tainted logic with tainted operand
|
||||
boolean logic = cond && safe();
|
||||
sink(logic);
|
||||
// tainted condition
|
||||
sink(concat.endsWith("I'm tainted"));
|
||||
|
||||
|
||||
// tainted
|
||||
logic = safe() || cond;
|
||||
sink(logic);
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
| B.java:15:21:15:27 | taint(...) | B.java:51:10:51:21 | fluentConcat |
|
||||
| B.java:15:21:15:27 | taint(...) | B.java:68:10:68:13 | cond |
|
||||
| B.java:15:21:15:27 | taint(...) | B.java:71:10:71:14 | logic |
|
||||
| B.java:15:21:15:27 | taint(...) | B.java:73:10:73:39 | endsWith(...) |
|
||||
| B.java:15:21:15:27 | taint(...) | B.java:76:10:76:14 | logic |
|
||||
| B.java:15:21:15:27 | taint(...) | B.java:79:10:79:14 | logic |
|
||||
| B.java:15:21:15:27 | taint(...) | B.java:87:10:87:16 | trimmed |
|
||||
|
||||
@@ -18,7 +18,7 @@ class ExternalApiUsage {
|
||||
AtomicReference<String> ref = new AtomicReference<>(); // not supported
|
||||
ref.set("foo");
|
||||
|
||||
String.class.isAssignableFrom(Object.class); // parameter with generic type
|
||||
String.class.isAssignableFrom(Object.class); // parameter with generic type, supported as a neutral model
|
||||
|
||||
System.out.println(d);
|
||||
System.out.println(map);
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
| java.lang.Class#isAssignableFrom(Class) | 1 |
|
||||
| java.time.Duration#ofMillis(long) | 1 |
|
||||
| java.util.concurrent.atomic.AtomicReference#set(Object) | 1 |
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
edges
|
||||
| Test.java:76:27:76:60 | getProperty(...) : String | Test.java:78:37:78:48 | userProperty : String |
|
||||
| Test.java:78:20:78:56 | parseInt(...) : Number | Test.java:80:31:80:34 | size |
|
||||
| Test.java:78:20:78:56 | parseInt(...) : Number | Test.java:86:34:86:37 | size |
|
||||
| Test.java:78:37:78:48 | userProperty : String | Test.java:78:37:78:55 | trim(...) : String |
|
||||
| Test.java:78:37:78:55 | trim(...) : String | Test.java:78:20:78:56 | parseInt(...) : Number |
|
||||
| Test.java:78:37:78:55 | trim(...) : String | Test.java:80:31:80:34 | size |
|
||||
| Test.java:78:37:78:55 | trim(...) : String | Test.java:86:34:86:37 | size |
|
||||
nodes
|
||||
| Test.java:76:27:76:60 | getProperty(...) : String | semmle.label | getProperty(...) : String |
|
||||
| Test.java:78:20:78:56 | parseInt(...) : Number | semmle.label | parseInt(...) : Number |
|
||||
| Test.java:78:37:78:48 | userProperty : String | semmle.label | userProperty : String |
|
||||
| Test.java:78:37:78:55 | trim(...) : String | semmle.label | trim(...) : String |
|
||||
| Test.java:80:31:80:34 | size | semmle.label | size |
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
edges
|
||||
| Test.java:14:27:14:60 | getProperty(...) : String | Test.java:16:38:16:49 | userProperty : String |
|
||||
| Test.java:16:21:16:57 | parseInt(...) : Number | Test.java:19:34:19:38 | index |
|
||||
| Test.java:16:38:16:49 | userProperty : String | Test.java:16:38:16:56 | trim(...) : String |
|
||||
| Test.java:16:38:16:56 | trim(...) : String | Test.java:16:21:16:57 | parseInt(...) : Number |
|
||||
| Test.java:16:38:16:56 | trim(...) : String | Test.java:19:34:19:38 | index |
|
||||
nodes
|
||||
| Test.java:14:27:14:60 | getProperty(...) : String | semmle.label | getProperty(...) : String |
|
||||
| Test.java:16:21:16:57 | parseInt(...) : Number | semmle.label | parseInt(...) : Number |
|
||||
| Test.java:16:38:16:49 | userProperty : String | semmle.label | userProperty : String |
|
||||
| Test.java:16:38:16:56 | trim(...) : String | semmle.label | trim(...) : String |
|
||||
| Test.java:19:34:19:38 | index | semmle.label | index |
|
||||
|
||||
@@ -11,19 +11,8 @@ edges
|
||||
| ArithmeticTainted.java:19:26:19:39 | readerBuffered : BufferedReader | ArithmeticTainted.java:19:26:19:50 | readLine(...) : String |
|
||||
| ArithmeticTainted.java:19:26:19:50 | readLine(...) : String | ArithmeticTainted.java:21:29:21:40 | stringNumber : String |
|
||||
| ArithmeticTainted.java:19:26:19:50 | readLine(...) : String | ArithmeticTainted.java:21:29:21:40 | stringNumber : String |
|
||||
| ArithmeticTainted.java:21:12:21:48 | parseInt(...) : Number | ArithmeticTainted.java:32:17:32:20 | data |
|
||||
| ArithmeticTainted.java:21:12:21:48 | parseInt(...) : Number | ArithmeticTainted.java:40:17:40:20 | data |
|
||||
| ArithmeticTainted.java:21:12:21:48 | parseInt(...) : Number | ArithmeticTainted.java:50:17:50:20 | data |
|
||||
| ArithmeticTainted.java:21:12:21:48 | parseInt(...) : Number | ArithmeticTainted.java:64:20:64:23 | data : Number |
|
||||
| ArithmeticTainted.java:21:12:21:48 | parseInt(...) : Number | ArithmeticTainted.java:95:37:95:40 | data |
|
||||
| ArithmeticTainted.java:21:12:21:48 | parseInt(...) : Number | ArithmeticTainted.java:118:9:118:12 | data : Number |
|
||||
| ArithmeticTainted.java:21:12:21:48 | parseInt(...) : Number | ArithmeticTainted.java:119:10:119:13 | data : Number |
|
||||
| ArithmeticTainted.java:21:12:21:48 | parseInt(...) : Number | ArithmeticTainted.java:120:10:120:13 | data : Number |
|
||||
| ArithmeticTainted.java:21:12:21:48 | parseInt(...) : Number | ArithmeticTainted.java:121:10:121:13 | data : Number |
|
||||
| ArithmeticTainted.java:21:29:21:40 | stringNumber : String | ArithmeticTainted.java:21:29:21:47 | trim(...) : String |
|
||||
| ArithmeticTainted.java:21:29:21:40 | stringNumber : String | ArithmeticTainted.java:21:29:21:47 | trim(...) : String |
|
||||
| ArithmeticTainted.java:21:29:21:47 | trim(...) : String | ArithmeticTainted.java:21:12:21:48 | parseInt(...) : Number |
|
||||
| ArithmeticTainted.java:21:29:21:47 | trim(...) : String | ArithmeticTainted.java:21:12:21:48 | parseInt(...) : Number |
|
||||
| ArithmeticTainted.java:21:29:21:47 | trim(...) : String | ArithmeticTainted.java:32:17:32:20 | data |
|
||||
| ArithmeticTainted.java:21:29:21:47 | trim(...) : String | ArithmeticTainted.java:40:17:40:20 | data |
|
||||
| ArithmeticTainted.java:21:29:21:47 | trim(...) : String | ArithmeticTainted.java:50:17:50:20 | data |
|
||||
@@ -64,8 +53,6 @@ nodes
|
||||
| ArithmeticTainted.java:19:26:19:39 | readerBuffered : BufferedReader | semmle.label | readerBuffered : BufferedReader |
|
||||
| ArithmeticTainted.java:19:26:19:50 | readLine(...) : String | semmle.label | readLine(...) : String |
|
||||
| ArithmeticTainted.java:19:26:19:50 | readLine(...) : String | semmle.label | readLine(...) : String |
|
||||
| ArithmeticTainted.java:21:12:21:48 | parseInt(...) : Number | semmle.label | parseInt(...) : Number |
|
||||
| ArithmeticTainted.java:21:12:21:48 | parseInt(...) : Number | semmle.label | parseInt(...) : Number |
|
||||
| ArithmeticTainted.java:21:29:21:40 | stringNumber : String | semmle.label | stringNumber : String |
|
||||
| ArithmeticTainted.java:21:29:21:40 | stringNumber : String | semmle.label | stringNumber : String |
|
||||
| ArithmeticTainted.java:21:29:21:47 | trim(...) : String | semmle.label | trim(...) : String |
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
| WebViewContentAccess.java:15:9:15:57 | setAllowContentAccess(...) | Sensitive information may be exposed via a malicious link due to access to content:// links being allowed in this WebView. |
|
||||
| WebViewContentAccess.java:38:9:38:55 | setAllowContentAccess(...) | Sensitive information may be exposed via a malicious link due to access to content:// links being allowed in this WebView. |
|
||||
| WebViewContentAccess.java:41:25:41:49 | (...)... | Sensitive information may be exposed via a malicious link due to access to content:// links being allowed in this WebView. |
|
||||
| WebViewContentAccess.java:43:9:43:44 | setAllowContentAccess(...) | Sensitive information may be exposed via a malicious link due to access to content:// links being allowed in this WebView. |
|
||||
| WebViewContentAccess.java:46:25:46:41 | new WebView(...) | Sensitive information may be exposed via a malicious link due to access to content:// links being allowed in this WebView. |
|
||||
| WebViewContentAccess.java:48:9:48:44 | setAllowContentAccess(...) | Sensitive information may be exposed via a malicious link due to access to content:// links being allowed in this WebView. |
|
||||
| WebViewContentAccess.java:51:25:51:44 | getAWebView(...) | Sensitive information may be exposed via a malicious link due to access to content:// links being allowed in this WebView. |
|
||||
| WebViewContentAccess.java:53:9:53:44 | setAllowContentAccess(...) | Sensitive information may be exposed via a malicious link due to access to content:// links being allowed in this WebView. |
|
||||
| WebViewContentAccess.java:55:29:55:48 | getAWebView(...) | Sensitive information may be exposed via a malicious link due to access to content:// links being allowed in this WebView. |
|
||||
| WebViewContentAccess.java:57:25:57:44 | getAWebView(...) | Sensitive information may be exposed via a malicious link due to access to content:// links being allowed in this WebView. |
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.example.test;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebSettings;
|
||||
|
||||
/** Helper class to mock a method which returns a `WebView` */
|
||||
interface WebViewGetter {
|
||||
WebView getAWebView();
|
||||
}
|
||||
|
||||
public class WebViewContentAccess extends Activity {
|
||||
void enableContentAccess(WebView webview) {
|
||||
webview.getSettings().setAllowContentAccess(true);
|
||||
}
|
||||
|
||||
void disableContentAccess(WebView webview) {
|
||||
webview.getSettings().setAllowContentAccess(false);
|
||||
}
|
||||
|
||||
void configureWebViewSafe(WebView view, WebViewGetter getter) {
|
||||
WebSettings settings = view.getSettings();
|
||||
|
||||
settings.setAllowContentAccess(false);
|
||||
|
||||
WebView view2 = (WebView) findViewById(0);
|
||||
settings = view2.getSettings();
|
||||
|
||||
settings.setAllowContentAccess(false);
|
||||
|
||||
disableContentAccess(getter.getAWebView());
|
||||
}
|
||||
|
||||
void configureWebViewUnsafe(WebView view1, WebViewGetter getter) {
|
||||
WebSettings settings;
|
||||
|
||||
view1.getSettings().setAllowContentAccess(true);
|
||||
|
||||
// Cast expression
|
||||
WebView view2 = (WebView) findViewById(0);
|
||||
settings = view2.getSettings();
|
||||
settings.setAllowContentAccess(true);
|
||||
|
||||
// Constructor
|
||||
WebView view3 = new WebView(this);
|
||||
settings = view3.getSettings();
|
||||
settings.setAllowContentAccess(true);
|
||||
|
||||
// Method access
|
||||
WebView view4 = getter.getAWebView();
|
||||
settings = view4.getSettings();
|
||||
settings.setAllowContentAccess(true);
|
||||
|
||||
enableContentAccess(getter.getAWebView());
|
||||
|
||||
WebView view5 = getter.getAWebView();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-200/AndroidWebViewSettingsAllowsContentAccess.ql
|
||||
@@ -1 +1 @@
|
||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/apache-commons-lang3-3.7/:${testdir}/../../../../../stubs/google-android-9.0.0
|
||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/apache-commons-lang3-3.7/:${testdir}/../../../../../stubs/google-android-9.0.0
|
||||
|
||||
@@ -166,6 +166,11 @@ module UnsafeShellCommandConstruction {
|
||||
.asExpr()
|
||||
.(BooleanLiteral)
|
||||
.getValue() = "true"
|
||||
or
|
||||
exists(API::Node node |
|
||||
node.asSink() = sys.getOptionsArg() and
|
||||
node.getMember("shell").asSink().mayHaveBooleanValue(true)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -282,6 +282,14 @@ nodes
|
||||
| lib/lib.js:543:23:543:26 | name |
|
||||
| lib/lib.js:545:23:545:26 | name |
|
||||
| lib/lib.js:545:23:545:26 | name |
|
||||
| lib/lib.js:550:39:550:42 | name |
|
||||
| lib/lib.js:550:39:550:42 | name |
|
||||
| lib/lib.js:551:33:551:36 | args |
|
||||
| lib/lib.js:552:23:552:26 | args |
|
||||
| lib/lib.js:552:23:552:26 | args |
|
||||
| lib/lib.js:555:25:555:37 | ["-rf", name] |
|
||||
| lib/lib.js:555:33:555:36 | name |
|
||||
| lib/lib.js:555:33:555:36 | name |
|
||||
| lib/subLib2/compiled-file.ts:3:26:3:29 | name |
|
||||
| lib/subLib2/compiled-file.ts:3:26:3:29 | name |
|
||||
| lib/subLib2/compiled-file.ts:4:25:4:28 | name |
|
||||
@@ -659,6 +667,14 @@ edges
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:545:23:545:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:545:23:545:26 | name |
|
||||
| lib/lib.js:509:39:509:42 | name | lib/lib.js:545:23:545:26 | name |
|
||||
| lib/lib.js:550:39:550:42 | name | lib/lib.js:555:33:555:36 | name |
|
||||
| lib/lib.js:550:39:550:42 | name | lib/lib.js:555:33:555:36 | name |
|
||||
| lib/lib.js:550:39:550:42 | name | lib/lib.js:555:33:555:36 | name |
|
||||
| lib/lib.js:550:39:550:42 | name | lib/lib.js:555:33:555:36 | name |
|
||||
| lib/lib.js:551:33:551:36 | args | lib/lib.js:552:23:552:26 | args |
|
||||
| lib/lib.js:551:33:551:36 | args | lib/lib.js:552:23:552:26 | args |
|
||||
| lib/lib.js:555:25:555:37 | ["-rf", name] | lib/lib.js:551:33:551:36 | args |
|
||||
| lib/lib.js:555:33:555:36 | name | lib/lib.js:555:25:555:37 | ["-rf", name] |
|
||||
| lib/subLib2/compiled-file.ts:3:26:3:29 | name | lib/subLib2/compiled-file.ts:4:25:4:28 | name |
|
||||
| lib/subLib2/compiled-file.ts:3:26:3:29 | name | lib/subLib2/compiled-file.ts:4:25:4:28 | name |
|
||||
| lib/subLib2/compiled-file.ts:3:26:3:29 | name | lib/subLib2/compiled-file.ts:4:25:4:28 | name |
|
||||
@@ -775,6 +791,8 @@ edges
|
||||
| lib/lib.js:537:11:537:26 | "rm -rf " + name | lib/lib.js:509:39:509:42 | name | lib/lib.js:537:23:537:26 | name | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:509:39:509:42 | name | library input | lib/lib.js:537:3:537:27 | cp.exec ... + name) | shell command |
|
||||
| lib/lib.js:543:11:543:26 | "rm -rf " + name | lib/lib.js:509:39:509:42 | name | lib/lib.js:543:23:543:26 | name | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:509:39:509:42 | name | library input | lib/lib.js:543:3:543:27 | cp.exec ... + name) | shell command |
|
||||
| lib/lib.js:545:11:545:26 | "rm -rf " + name | lib/lib.js:509:39:509:42 | name | lib/lib.js:545:23:545:26 | name | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:509:39:509:42 | name | library input | lib/lib.js:545:3:545:27 | cp.exec ... + name) | shell command |
|
||||
| lib/lib.js:552:23:552:26 | args | lib/lib.js:550:39:550:42 | name | lib/lib.js:552:23:552:26 | args | This shell argument which depends on $@ is later used in a $@. | lib/lib.js:550:39:550:42 | name | library input | lib/lib.js:552:9:552:38 | cp.spaw ... wnOpts) | shell command |
|
||||
| lib/lib.js:555:33:555:36 | name | lib/lib.js:550:39:550:42 | name | lib/lib.js:555:33:555:36 | name | This shell argument which depends on $@ is later used in a $@. | lib/lib.js:550:39:550:42 | name | library input | lib/lib.js:552:9:552:38 | cp.spaw ... wnOpts) | shell command |
|
||||
| lib/subLib2/compiled-file.ts:4:13:4:28 | "rm -rf " + name | lib/subLib2/compiled-file.ts:3:26:3:29 | name | lib/subLib2/compiled-file.ts:4:25:4:28 | name | This string concatenation which depends on $@ is later used in a $@. | lib/subLib2/compiled-file.ts:3:26:3:29 | name | library input | lib/subLib2/compiled-file.ts:4:5:4:29 | cp.exec ... + name) | shell command |
|
||||
| lib/subLib2/special-file.js:4:10:4:25 | "rm -rf " + name | lib/subLib2/special-file.js:3:28:3:31 | name | lib/subLib2/special-file.js:4:22:4:25 | name | This string concatenation which depends on $@ is later used in a $@. | lib/subLib2/special-file.js:3:28:3:31 | name | library input | lib/subLib2/special-file.js:4:2:4:26 | cp.exec ... + name) | shell command |
|
||||
| lib/subLib3/my-file.ts:4:10:4:25 | "rm -rf " + name | lib/subLib3/my-file.ts:3:28:3:31 | name | lib/subLib3/my-file.ts:4:22:4:25 | name | This string concatenation which depends on $@ is later used in a $@. | lib/subLib3/my-file.ts:3:28:3:31 | name | library input | lib/subLib3/my-file.ts:4:2:4:26 | cp.exec ... + name) | shell command |
|
||||
|
||||
@@ -545,3 +545,12 @@ module.exports.sanitizer4 = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports.shellThing = function (name) {
|
||||
function indirectShell(cmd, args, spawnOpts) {
|
||||
cp.spawn(cmd, args, spawnOpts); // NOT OK
|
||||
}
|
||||
|
||||
indirectShell("rm", ["-rf", name], {shell: true});
|
||||
}
|
||||
@@ -1,47 +1,9 @@
|
||||
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
|
||||
|
||||
_swift_prebuilt_version = "swift-5.7.1-RELEASE.44582.104"
|
||||
_swift_sha_map = {
|
||||
"Linux-X64": "8d94f2d75f2aa9ee8e5421318d2f07b27e095127c9be0156794a88d8e9a0f19a",
|
||||
"macOS-X64": "5f0550d2924e7071d006a0c9802acbd9a11f0017073e4a1eb27b7ddc4764f3f2",
|
||||
}
|
||||
|
||||
_swift_arch_map = {
|
||||
"Linux-X64": "linux",
|
||||
"macOS-X64": "darwin_x86_64",
|
||||
}
|
||||
load("//swift/third_party:load.bzl", load_swift_dependencies = "load_dependencies")
|
||||
|
||||
def codeql_workspace(repository_name = "codeql"):
|
||||
for repo_arch, arch in _swift_arch_map.items():
|
||||
sha256 = _swift_sha_map[repo_arch]
|
||||
|
||||
http_archive(
|
||||
name = "swift_prebuilt_%s" % arch,
|
||||
url = "https://github.com/dsp-testing/codeql-swift-artifacts/releases/download/%s/swift-prebuilt-%s.zip" % (
|
||||
_swift_prebuilt_version,
|
||||
repo_arch,
|
||||
),
|
||||
build_file = "@%s//swift/third_party/swift-llvm-support:BUILD.swift-prebuilt.bazel" % repository_name,
|
||||
sha256 = sha256,
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "fishhook",
|
||||
url = "https://github.com/facebook/fishhook/archive/aadc161ac3b80db07a9908851839a17ba63a9eb1.zip",
|
||||
build_file = "@%s//swift/third_party/fishhook:BUILD.fishhook.bazel" % repository_name,
|
||||
strip_prefix = "fishhook-aadc161ac3b80db07a9908851839a17ba63a9eb1",
|
||||
sha256 = "9f2cdee6dcc2039d4c47d25ab5141fe0678ce6ed27ef482cab17fe9fa38a30ce",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "picosha2",
|
||||
url = "https://github.com/okdshin/PicoSHA2/archive/27fcf6979298949e8a462e16d09a0351c18fcaf2.zip",
|
||||
strip_prefix = "PicoSHA2-27fcf6979298949e8a462e16d09a0351c18fcaf2",
|
||||
build_file = "@%s//swift/third_party/picosha2:BUILD.picosha2.bazel" % repository_name,
|
||||
sha256 = "d6647ca45a8b7bdaf027ecb68d041b22a899a0218b7206dee755c558a2725abb",
|
||||
)
|
||||
|
||||
load_swift_dependencies(repository_name)
|
||||
maybe(
|
||||
repo_rule = http_archive,
|
||||
name = "rules_pkg",
|
||||
|
||||
@@ -45,6 +45,16 @@ module Consistency {
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodeAtPosition`. */
|
||||
predicate uniqueParameterNodeAtPositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodePosition`. */
|
||||
predicate uniqueParameterNodePositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
private class RelevantNode extends Node {
|
||||
@@ -246,6 +256,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodeAtPosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodeAtPositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and
|
||||
msg = "Parameters with overlapping positions."
|
||||
@@ -254,6 +265,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodePosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodePositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
|
||||
@@ -101,19 +101,15 @@ class Configuration extends TaintTracking::Configuration {
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
exists(AttrRead attr, MethodCallNode call |
|
||||
attr.accesses(nodeFrom, "getmembers") and
|
||||
nodeFrom = call.getObject() and
|
||||
nodeFrom instanceof AllTarfileOpens and
|
||||
nodeTo = call
|
||||
)
|
||||
nodeTo.(MethodCallNode).calls(nodeFrom, "getmembers") and
|
||||
nodeFrom instanceof AllTarfileOpens
|
||||
or
|
||||
exists(API::CallNode closing |
|
||||
closing = API::moduleImport("contextlib").getMember("closing").getACall() and
|
||||
nodeFrom = closing.getArg(0) and
|
||||
nodeFrom = tarfileOpen().getReturn().getAValueReachingSink() and
|
||||
nodeTo = closing
|
||||
)
|
||||
// To handle the case of `with closing(tarfile.open()) as file:`
|
||||
// we add a step from the first argument of `closing` to the call to `closing`,
|
||||
// whenever that first argument is a return of `tarfile.open()`.
|
||||
nodeTo = API::moduleImport("contextlib").getMember("closing").getACall() and
|
||||
nodeFrom = nodeTo.(API::CallNode).getArg(0) and
|
||||
nodeFrom = tarfileOpen().getReturn().getAValueReachableFromSource()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ edges
|
||||
| TarSlipImprov.py:133:7:133:39 | ControlFlowNode for Attribute() | TarSlipImprov.py:134:1:134:3 | ControlFlowNode for tar |
|
||||
| TarSlipImprov.py:141:6:141:29 | ControlFlowNode for Attribute() | TarSlipImprov.py:142:9:142:13 | GSSA Variable entry |
|
||||
| TarSlipImprov.py:142:9:142:13 | GSSA Variable entry | TarSlipImprov.py:143:36:143:40 | ControlFlowNode for entry |
|
||||
| TarSlipImprov.py:159:26:159:51 | ControlFlowNode for Attribute() | TarSlipImprov.py:169:9:169:12 | ControlFlowNode for tarc |
|
||||
| TarSlipImprov.py:176:6:176:31 | ControlFlowNode for Attribute() | TarSlipImprov.py:177:9:177:13 | GSSA Variable entry |
|
||||
| TarSlipImprov.py:177:9:177:13 | GSSA Variable entry | TarSlipImprov.py:178:36:178:40 | ControlFlowNode for entry |
|
||||
| TarSlipImprov.py:182:6:182:31 | ControlFlowNode for Attribute() | TarSlipImprov.py:183:9:183:13 | GSSA Variable entry |
|
||||
@@ -71,6 +72,8 @@ nodes
|
||||
| TarSlipImprov.py:141:6:141:29 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| TarSlipImprov.py:142:9:142:13 | GSSA Variable entry | semmle.label | GSSA Variable entry |
|
||||
| TarSlipImprov.py:143:36:143:40 | ControlFlowNode for entry | semmle.label | ControlFlowNode for entry |
|
||||
| TarSlipImprov.py:159:26:159:51 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| TarSlipImprov.py:169:9:169:12 | ControlFlowNode for tarc | semmle.label | ControlFlowNode for tarc |
|
||||
| TarSlipImprov.py:176:6:176:31 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| TarSlipImprov.py:177:9:177:13 | GSSA Variable entry | semmle.label | GSSA Variable entry |
|
||||
| TarSlipImprov.py:178:36:178:40 | ControlFlowNode for entry | semmle.label | ControlFlowNode for entry |
|
||||
@@ -124,6 +127,7 @@ subpaths
|
||||
| TarSlipImprov.py:130:5:130:7 | ControlFlowNode for tar | TarSlipImprov.py:129:6:129:26 | ControlFlowNode for Attribute() | TarSlipImprov.py:130:5:130:7 | ControlFlowNode for tar | Extraction of tarfile from $@ to a potentially untrusted source $@. | TarSlipImprov.py:129:6:129:26 | ControlFlowNode for Attribute() | ControlFlowNode for Attribute() | TarSlipImprov.py:130:5:130:7 | ControlFlowNode for tar | ControlFlowNode for tar |
|
||||
| TarSlipImprov.py:134:1:134:3 | ControlFlowNode for tar | TarSlipImprov.py:133:7:133:39 | ControlFlowNode for Attribute() | TarSlipImprov.py:134:1:134:3 | ControlFlowNode for tar | Extraction of tarfile from $@ to a potentially untrusted source $@. | TarSlipImprov.py:133:7:133:39 | ControlFlowNode for Attribute() | ControlFlowNode for Attribute() | TarSlipImprov.py:134:1:134:3 | ControlFlowNode for tar | ControlFlowNode for tar |
|
||||
| TarSlipImprov.py:143:36:143:40 | ControlFlowNode for entry | TarSlipImprov.py:141:6:141:29 | ControlFlowNode for Attribute() | TarSlipImprov.py:143:36:143:40 | ControlFlowNode for entry | Extraction of tarfile from $@ to a potentially untrusted source $@. | TarSlipImprov.py:141:6:141:29 | ControlFlowNode for Attribute() | ControlFlowNode for Attribute() | TarSlipImprov.py:143:36:143:40 | ControlFlowNode for entry | ControlFlowNode for entry |
|
||||
| TarSlipImprov.py:169:9:169:12 | ControlFlowNode for tarc | TarSlipImprov.py:159:26:159:51 | ControlFlowNode for Attribute() | TarSlipImprov.py:169:9:169:12 | ControlFlowNode for tarc | Extraction of tarfile from $@ to a potentially untrusted source $@. | TarSlipImprov.py:159:26:159:51 | ControlFlowNode for Attribute() | ControlFlowNode for Attribute() | TarSlipImprov.py:169:9:169:12 | ControlFlowNode for tarc | ControlFlowNode for tarc |
|
||||
| TarSlipImprov.py:178:36:178:40 | ControlFlowNode for entry | TarSlipImprov.py:176:6:176:31 | ControlFlowNode for Attribute() | TarSlipImprov.py:178:36:178:40 | ControlFlowNode for entry | Extraction of tarfile from $@ to a potentially untrusted source $@. | TarSlipImprov.py:176:6:176:31 | ControlFlowNode for Attribute() | ControlFlowNode for Attribute() | TarSlipImprov.py:178:36:178:40 | ControlFlowNode for entry | ControlFlowNode for entry |
|
||||
| TarSlipImprov.py:184:21:184:25 | ControlFlowNode for entry | TarSlipImprov.py:182:6:182:31 | ControlFlowNode for Attribute() | TarSlipImprov.py:184:21:184:25 | ControlFlowNode for entry | Extraction of tarfile from $@ to a potentially untrusted source $@. | TarSlipImprov.py:182:6:182:31 | ControlFlowNode for Attribute() | ControlFlowNode for Attribute() | TarSlipImprov.py:184:21:184:25 | ControlFlowNode for entry | ControlFlowNode for entry |
|
||||
| TarSlipImprov.py:189:1:189:3 | ControlFlowNode for tar | TarSlipImprov.py:188:7:188:27 | ControlFlowNode for Attribute() | TarSlipImprov.py:189:1:189:3 | ControlFlowNode for tar | Extraction of tarfile from $@ to a potentially untrusted source $@. | TarSlipImprov.py:188:7:188:27 | ControlFlowNode for Attribute() | ControlFlowNode for Attribute() | TarSlipImprov.py:189:1:189:3 | ControlFlowNode for tar | ControlFlowNode for tar |
|
||||
|
||||
7
ql/rust-toolchain.toml
Normal file
7
ql/rust-toolchain.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
# This file specifies the Rust version used to develop and test the QL
|
||||
# extractor. It is set to the lowest version of Rust we want to support.
|
||||
|
||||
[toolchain]
|
||||
channel = "1.54"
|
||||
profile = "minimal"
|
||||
components = [ "rustfmt" ]
|
||||
@@ -8,6 +8,7 @@ type NUL && "%CODEQL_DIST%\codeql.exe" database index-files ^
|
||||
--include-extension=.yml ^
|
||||
--size-limit=5m ^
|
||||
--language=ql ^
|
||||
--working-dir=. ^
|
||||
"%CODEQL_EXTRACTOR_QL_WIP_DATABASE%"
|
||||
|
||||
exit /b %ERRORLEVEL%
|
||||
|
||||
@@ -45,6 +45,16 @@ module Consistency {
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodeAtPosition`. */
|
||||
predicate uniqueParameterNodeAtPositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodePosition`. */
|
||||
predicate uniqueParameterNodePositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
private class RelevantNode extends Node {
|
||||
@@ -246,6 +256,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodeAtPosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodeAtPositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and
|
||||
msg = "Parameters with overlapping positions."
|
||||
@@ -254,6 +265,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodePosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodePositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for reasoning about
|
||||
* code constructed from library input vulnerabilities, as
|
||||
* well as extension points for adding your own.
|
||||
*/
|
||||
|
||||
private import ruby
|
||||
private import codeql.ruby.ApiGraphs
|
||||
private import codeql.ruby.frameworks.core.Gem::Gem as Gem
|
||||
private import codeql.ruby.Concepts as Concepts
|
||||
|
||||
/**
|
||||
* Module containing sources, sinks, and sanitizers for code constructed from library input.
|
||||
*/
|
||||
module UnsafeCodeConstruction {
|
||||
/** A source for code constructed from library input vulnerabilities. */
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/** An input parameter to a gem seen as a source. */
|
||||
private class LibraryInputAsSource extends Source instanceof DataFlow::ParameterNode {
|
||||
LibraryInputAsSource() {
|
||||
this = Gem::getALibraryInput() and
|
||||
not this.getName() = "code"
|
||||
}
|
||||
}
|
||||
|
||||
/** A sink for code constructed from library input vulnerabilities. */
|
||||
abstract class Sink extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the node where the unsafe code is executed.
|
||||
*/
|
||||
abstract DataFlow::Node getCodeSink();
|
||||
|
||||
/**
|
||||
* Gets the type of sink.
|
||||
*/
|
||||
string getSinkType() { result = "code construction" }
|
||||
}
|
||||
|
||||
/** Gets a node that is eventually executed as code at `codeExec`. */
|
||||
DataFlow::Node getANodeExecutedAsCode(Concepts::CodeExecution codeExec) {
|
||||
result = getANodeExecutedAsCode(TypeTracker::TypeBackTracker::end(), codeExec)
|
||||
}
|
||||
|
||||
import codeql.ruby.typetracking.TypeTracker as TypeTracker
|
||||
|
||||
/** Gets a node that is eventually executed as code at `codeExec`, type-tracked with `t`. */
|
||||
private DataFlow::LocalSourceNode getANodeExecutedAsCode(
|
||||
TypeTracker::TypeBackTracker t, Concepts::CodeExecution codeExec
|
||||
) {
|
||||
t.start() and
|
||||
result = codeExec.getCode().getALocalSource() and
|
||||
codeExec.runsArbitraryCode() // methods like `Object.send` is benign here, because of the string-construction the attacker cannot control the entire method name
|
||||
or
|
||||
exists(TypeTracker::TypeBackTracker t2 |
|
||||
result = getANodeExecutedAsCode(t2, codeExec).backtrack(t2, t)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A string constructed using a `.join(...)` call, where the resulting string ends up being executed as code.
|
||||
*/
|
||||
class ArrayJoin extends Sink {
|
||||
Concepts::CodeExecution s;
|
||||
|
||||
ArrayJoin() {
|
||||
exists(DataFlow::CallNode call |
|
||||
call.getMethodName() = "join" and
|
||||
call.getNumberOfArguments() = 1 and // any string. E.g. ";" or "\n".
|
||||
call = getANodeExecutedAsCode(s) and
|
||||
this = call.getReceiver()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getCodeSink() { result = s }
|
||||
|
||||
override string getSinkType() { result = "array" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A string constructed from a string-literal (e.g. `"foo #{sink}"`),
|
||||
* where the resulting string ends up being executed as a code.
|
||||
*/
|
||||
class StringInterpolationAsSink extends Sink {
|
||||
Concepts::CodeExecution s;
|
||||
|
||||
StringInterpolationAsSink() {
|
||||
exists(Ast::StringlikeLiteral lit |
|
||||
any(DataFlow::Node n | n.asExpr().getExpr() = lit) = getANodeExecutedAsCode(s) and
|
||||
this.asExpr().getExpr() = lit.getComponent(_)
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getCodeSink() { result = s }
|
||||
|
||||
override string getSinkType() { result = "string interpolation" }
|
||||
}
|
||||
|
||||
import codeql.ruby.security.TaintedFormatStringSpecific as TaintedFormat
|
||||
|
||||
/**
|
||||
* A string constructed from a printf-style call,
|
||||
* where the resulting string ends up being executed as a code.
|
||||
*/
|
||||
class TaintedFormatStringAsSink extends Sink {
|
||||
Concepts::CodeExecution s;
|
||||
|
||||
TaintedFormatStringAsSink() {
|
||||
exists(TaintedFormat::PrintfStyleCall call |
|
||||
call = getANodeExecutedAsCode(s) and
|
||||
this = [call.getFormatArgument(_), call.getFormatString()]
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getCodeSink() { result = s }
|
||||
|
||||
override string getSinkType() { result = "string format" }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Provides a taint-tracking configuration for reasoning about code
|
||||
* constructed from library input vulnerabilities.
|
||||
*
|
||||
* Note, for performance reasons: only import this file if `Configuration` is needed,
|
||||
* otherwise `UnsafeCodeConstructionCustomizations` should be imported instead.
|
||||
*/
|
||||
|
||||
import codeql.ruby.DataFlow
|
||||
import UnsafeCodeConstructionCustomizations::UnsafeCodeConstruction
|
||||
private import codeql.ruby.TaintTracking
|
||||
private import codeql.ruby.dataflow.BarrierGuards
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for detecting code constructed from library input vulnerabilities.
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "UnsafeShellCommandConstruction" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
node instanceof StringConstCompareBarrier or
|
||||
node instanceof StringConstArrayInclusionCallBarrier
|
||||
}
|
||||
|
||||
// override to require the path doesn't have unmatched return steps
|
||||
override DataFlow::FlowFeature getAFeature() {
|
||||
result instanceof DataFlow::FeatureHasSourceCallContext
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
// if an array element gets tainted, then we treat the entire array as tainted
|
||||
exists(DataFlow::CallNode call |
|
||||
call.getMethodName() = ["<<", "push", "append"] and
|
||||
call.getReceiver() = succ and
|
||||
pred = call.getArgument(0) and
|
||||
call.getNumberOfArguments() = 1
|
||||
)
|
||||
or
|
||||
exists(DataFlow::CallNode call |
|
||||
call.getMethodName() = "[]" and
|
||||
succ = call and
|
||||
pred = call.getArgument(_)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,7 @@ module UnsafeShellCommandConstruction {
|
||||
*/
|
||||
class StringInterpolationAsSink extends Sink {
|
||||
Concepts::SystemCommandExecution s;
|
||||
Ast::StringLiteral lit;
|
||||
Ast::StringlikeLiteral lit;
|
||||
|
||||
StringInterpolationAsSink() {
|
||||
isUsedAsShellCommand(any(DataFlow::Node n | n.asExpr().getExpr() = lit), s) and
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
* Added a new query, `rb/unsafe-code-construction`, to detect libraries that unsafely construct code from their inputs.
|
||||
@@ -0,0 +1,111 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
When a library function dynamically constructs code in a potentially unsafe way,
|
||||
it's important to document to clients of the library that the function should only be
|
||||
used with trusted inputs.
|
||||
|
||||
If the function is not documented as being potentially unsafe, then a client may
|
||||
incorrectly use inputs containing unsafe code fragments, and thereby leave the
|
||||
client vulnerable to code-injection attacks.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Properly document library functions that construct code from unsanitized
|
||||
inputs, or avoid constructing code in the first place.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
The following example shows two methods implemented using <code>eval</code>: a simple
|
||||
deserialization routine and a getter method.
|
||||
If untrusted inputs are used with these methods,
|
||||
then an attacker might be able to execute arbitrary code on the system.
|
||||
</p>
|
||||
|
||||
<sample src="examples/UnsafeCodeConstruction.rb" />
|
||||
|
||||
<p>
|
||||
To avoid this problem, either properly document that the function is potentially
|
||||
unsafe, or use an alternative solution such as <code>JSON.parse</code> or another library
|
||||
that does not allow arbitrary code to be executed.
|
||||
</p>
|
||||
|
||||
<sample src="examples/UnsafeCodeConstructionSafe.rb" />
|
||||
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
As another example, consider the below code which dynamically constructs
|
||||
a class that has a getter method with a custom name.
|
||||
</p>
|
||||
|
||||
<sample src="examples/UnsafeCodeConstruction2.rb" />
|
||||
|
||||
<p>
|
||||
The example dynamically constructs a string which is then executed using <code>module_eval</code>.
|
||||
This code will break if the specified name is not a valid Ruby identifier, and
|
||||
if the value is controlled by an attacker, then this could lead to code-injection.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
A more robust implementation, that is also immune to code-injection,
|
||||
can be made by using <code>module_eval</code> with a block and using <code>define_method</code>
|
||||
to define the getter method.
|
||||
</p>
|
||||
|
||||
<sample src="examples/UnsafeCodeConstruction2Safe.rb" />
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
This example dynamically registers a method on another class which
|
||||
forwards its arguments to a target object. This approach uses
|
||||
<code>module_eval</code> and string interpolation to construct class variables
|
||||
and methods.
|
||||
</p>
|
||||
|
||||
<sample src="examples/UnsafeCodeConstruction3.rb" />
|
||||
|
||||
<p>
|
||||
A safer approach is to use <code>class_variable_set</code> and
|
||||
<code>class_variable_get</code> along with <code>define_method</code>. String
|
||||
interpolation is still used to construct the class variable name, but this is
|
||||
safe because <code>class_variable_set</code> is not susceptible to code-injection.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<code>send</code> is used to dynamically call the method specified by <code>name</code>.
|
||||
This is a more robust alternative than the previous example, because it does not allow
|
||||
arbitrary code to be executed, but it does still allow for any method to be called
|
||||
on the target object.
|
||||
</p>
|
||||
|
||||
<sample src="examples/UnsafeCodeConstruction3Safe.rb" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
OWASP:
|
||||
<a href="https://www.owasp.org/index.php/Code_Injection">Code Injection</a>.
|
||||
</li>
|
||||
<li>
|
||||
Wikipedia: <a href="https://en.wikipedia.org/wiki/Code_injection">Code Injection</a>.
|
||||
</li>
|
||||
<li>
|
||||
Ruby documentation: <a href="https://docs.ruby-lang.org/en/3.2/Module.html#method-i-define_method">define_method</a>.
|
||||
</li>
|
||||
<li>
|
||||
Ruby documentation: <a href="https://docs.ruby-lang.org/en/3.2/Module.html#method-i-class_variable_set">class_variable_set</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* @name Unsafe code constructed from library input
|
||||
* @description Using externally controlled strings to construct code may allow a malicious
|
||||
* user to execute arbitrary code.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 6.1
|
||||
* @precision medium
|
||||
* @id rb/unsafe-code-construction
|
||||
* @tags security
|
||||
* external/cwe/cwe-094
|
||||
* external/cwe/cwe-079
|
||||
* external/cwe/cwe-116
|
||||
*/
|
||||
|
||||
import codeql.ruby.security.UnsafeCodeConstructionQuery
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, Sink sinkNode
|
||||
where cfg.hasFlowPath(source, sink) and sinkNode = sink.getNode()
|
||||
select sink.getNode(), source, sink,
|
||||
"This " + sinkNode.getSinkType() + " which depends on $@ is later $@.", source.getNode(),
|
||||
"library input", sinkNode.getCodeSink(), "interpreted as code"
|
||||
@@ -0,0 +1,10 @@
|
||||
module MyLib
|
||||
def unsafeDeserialize(value)
|
||||
eval("foo = #{value}")
|
||||
foo
|
||||
end
|
||||
|
||||
def unsafeGetter(obj, path)
|
||||
eval("obj.#{path}")
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
require 'json'
|
||||
|
||||
module BadMakeGetter
|
||||
# Makes a class with a method named `getter_name` that returns `val`
|
||||
def self.define_getter_class(getter_name, val)
|
||||
new_class = Class.new
|
||||
new_class.module_eval <<-END
|
||||
def #{getter_name}
|
||||
#{JSON.dump(val)}
|
||||
end
|
||||
END
|
||||
new_class
|
||||
end
|
||||
end
|
||||
|
||||
one = BadMakeGetter.define_getter_class(:one, "foo")
|
||||
puts "One is #{one.new.one}"
|
||||
@@ -0,0 +1,13 @@
|
||||
# Uses `define_method` instead of constructing a string
|
||||
module GoodMakeGetter
|
||||
def self.define_getter_class(getter_name, val)
|
||||
new_class = Class.new
|
||||
new_class.module_eval do
|
||||
define_method(getter_name) { val }
|
||||
end
|
||||
new_class
|
||||
end
|
||||
end
|
||||
|
||||
two = GoodMakeGetter.define_getter_class(:two, "bar")
|
||||
puts "Two is #{two.new.two}"
|
||||
@@ -0,0 +1,11 @@
|
||||
module Invoker
|
||||
def attach(klass, name, target)
|
||||
klass.module_eval <<-CODE
|
||||
@@#{name} = target
|
||||
|
||||
def #{name}(*args)
|
||||
@@#{name}.#{name}(*args)
|
||||
end
|
||||
CODE
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
module Invoker
|
||||
def attach(klass, name, target)
|
||||
var = :"@@#{name}"
|
||||
klass.class_variable_set(var, target)
|
||||
klass.define_method(name) do |*args|
|
||||
self.class.class_variable_get(var).send(name, *args)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,11 @@
|
||||
require 'json'
|
||||
|
||||
module MyLib
|
||||
def safeDeserialize(value)
|
||||
JSON.parse(value)
|
||||
end
|
||||
|
||||
def safeGetter(obj, path)
|
||||
obj.dig(*path.split("."))
|
||||
end
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user