Merge branch 'main' into coredata

This commit is contained in:
Geoffrey White
2023-01-18 13:39:02 +00:00
committed by GitHub
205 changed files with 30116 additions and 882 deletions

View File

@@ -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

View File

@@ -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: |

View File

@@ -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."

View File

@@ -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."

View File

@@ -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."

View File

@@ -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(), "|") + ")\"")
)
}

View File

@@ -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>:

View File

@@ -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];
}
}

View 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];
}
}

View File

@@ -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 |

View File

@@ -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()

View File

@@ -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;
}

View File

@@ -1,7 +1,7 @@
struct Foo {
~Foo();
struct ArrayDelete {
~ArrayDelete();
};
void f() {
delete[] (Foo*)nullptr;
delete[] (ArrayDelete*)nullptr;
}

View File

@@ -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. |

View File

@@ -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. |

View File

@@ -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 |

View File

@@ -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

View File

@@ -0,0 +1,2 @@
description: Remove operators from the virtualizable type.
compatibility: full

View File

@@ -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;
}
}

View File

@@ -0,0 +1,5 @@
---
category: minorAnalysis
---
* Support for `static virtual` and `static abstract` interface members.
* Support for *operators* in interface definitions.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Attributes on methods in CIL are now extracted (Bugfix).

View File

@@ -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()

View File

@@ -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.
*

View File

@@ -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."

View File

@@ -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

View File

@@ -0,0 +1,2 @@
description: Add operators to the virtualizable type.
compatibility: full

File diff suppressed because it is too large Load Diff

View File

@@ -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>"
] + "%")
}

View File

@@ -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 };
}

View File

@@ -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 |

View File

@@ -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
}

View File

@@ -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

View File

@@ -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

View File

@@ -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
1 package sink source summary sink:bean-validation sink:create-file sink:fragment-injection sink:groovy sink:header-splitting sink:information-leak sink:intent-start sink:jdbc-url sink:jexl sink:jndi-injection sink:ldap sink:logging sink:mvel sink:ognl-injection sink:open-url sink:pending-intent-sent sink:regex-use sink:regex-use[-1] sink:regex-use[0] sink:regex-use[] sink:regex-use[f-1] sink:regex-use[f1] sink:regex-use[f] sink:set-hostname-verifier sink:sql sink:ssti sink:url-open-stream sink:url-redirect sink:write-file sink:xpath sink:xslt sink:xss source:android-external-storage-dir source:android-widget source:contentprovider source:remote summary:taint summary:value
38 jakarta.ws.rs.container 9 9
39 jakarta.ws.rs.core 2 149 2 94 55
40 java.beans 1 1
41 java.io 37 40 42 15 22 40 41 1
42 java.lang 13 75 76 8 4 1 56 53 19 23
java.math 1 1
43 java.net 10 3 7 10 3 7
44 java.nio 15 16 13 2 16
45 java.sql 11 1 2 4 7 1 1
46 java.util 44 461 465 34 5 2 1 2 36 38 425 427
47 javax.faces.context 2 7 2 7
48 javax.jms 9 57 9 57
49 javax.json 123 100 23

View File

@@ -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

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added `AllowContentAccessMethod` to represent the `setAllowContentAccess` method of the `android.webkit.WebSettings` class.

View File

@@ -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`.

View File

@@ -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"]

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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

View 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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -93,3 +93,4 @@ extensions:
extensible: neutralModel
data:
- ["java.util.stream", "Collectors", "toList", "()", "manual"]
- ["java.util.stream", "Collectors", "toSet", "()", "manual"]

View File

@@ -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."

View File

@@ -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() {

View File

@@ -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)
}
}

View File

@@ -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());
}
}

View File

@@ -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.

View File

@@ -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>

View File

@@ -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."

View File

@@ -0,0 +1,3 @@
WebSettings settings = webview.getSettings();
settings.setAllowContentAccess(false);

View File

@@ -0,0 +1,3 @@
WebSettings settings = webview.getSettings();
settings.setAllowContentAccess(true);

View File

@@ -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.

View File

@@ -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 |

View File

@@ -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(...) |

View File

@@ -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
}
}
}

View File

@@ -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
*/
}

View File

@@ -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 |

View File

@@ -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 { }

View File

@@ -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);

View File

@@ -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 |

View File

@@ -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);

View File

@@ -1,3 +1,2 @@
| java.lang.Class#isAssignableFrom(Class) | 1 |
| java.time.Duration#ofMillis(long) | 1 |
| java.util.concurrent.atomic.AtomicReference#set(Object) | 1 |

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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. |

View File

@@ -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();
}
}

View File

@@ -0,0 +1 @@
Security/CWE/CWE-200/AndroidWebViewSettingsAllowsContentAccess.ql

View File

@@ -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

View File

@@ -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)
)
}
/**

View File

@@ -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 |

View File

@@ -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});
}

View File

@@ -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",

View File

@@ -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."

View File

@@ -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()
}
}

View File

@@ -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
View 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" ]

View File

@@ -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%

View File

@@ -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."

View File

@@ -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" }
}
}

View File

@@ -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(_)
)
}
}

View File

@@ -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

View File

@@ -0,0 +1,4 @@
---
category: newQuery
---
* Added a new query, `rb/unsafe-code-construction`, to detect libraries that unsafely construct code from their inputs.

View File

@@ -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>

View File

@@ -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"

View File

@@ -0,0 +1,10 @@
module MyLib
def unsafeDeserialize(value)
eval("foo = #{value}")
foo
end
def unsafeGetter(obj, path)
eval("obj.#{path}")
end
end

View File

@@ -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}"

View File

@@ -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}"

View File

@@ -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

View File

@@ -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

View File

@@ -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