diff --git a/.github/workflows/compile-queries.yml b/.github/workflows/compile-queries.yml index 73d1e5ac2cc..bdcf0ba8d8a 100644 --- a/.github/workflows/compile-queries.yml +++ b/.github/workflows/compile-queries.yml @@ -46,7 +46,7 @@ jobs: with: channel: 'release' - name: check formatting - run: codeql query format */ql/{src,lib,test}/**/*.{qll,ql} --check-only + run: find */ql -type f \( -name "*.qll" -o -name "*.ql" \) -print0 | xargs -0 codeql query format --check-only - name: compile queries - check-only # run with --check-only if running in a PR (github.sha != main) if : ${{ github.event_name == 'pull_request' }} diff --git a/.github/workflows/js-ml-tests.yml b/.github/workflows/js-ml-tests.yml index c932432530b..d3b5d49f2ba 100644 --- a/.github/workflows/js-ml-tests.yml +++ b/.github/workflows/js-ml-tests.yml @@ -23,19 +23,6 @@ defaults: working-directory: javascript/ql/experimental/adaptivethreatmodeling jobs: - qlformat: - name: Check QL formatting - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - uses: ./.github/actions/fetch-codeql - - - name: Check QL formatting - run: | - find . "(" -name "*.ql" -or -name "*.qll" ")" -print0 | \ - xargs -0 codeql query format --check-only - qlcompile: name: Check QL compilation runs-on: ubuntu-latest diff --git a/.github/workflows/ruby-qltest.yml b/.github/workflows/ruby-qltest.yml index 97235b722ba..125e2694fb0 100644 --- a/.github/workflows/ruby-qltest.yml +++ b/.github/workflows/ruby-qltest.yml @@ -28,13 +28,6 @@ defaults: working-directory: ruby jobs: - qlformat: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: ./.github/actions/fetch-codeql - - name: Check QL formatting - run: find ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql query format --check-only qlcompile: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 873b94d2118..09e1c8a92f8 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -112,12 +112,9 @@ jobs: with: name: swift-generated-cpp-files path: swift/generated-cpp-files/** - qlformat: + database-upgrade-scripts: runs-on: ubuntu-latest - needs: changes - if: ${{ needs.changes.outputs.ql == 'true' }} steps: - uses: actions/checkout@v3 - uses: ./.github/actions/fetch-codeql - - name: Check QL formatting - run: find swift/ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql query format --check-only + - uses: ./swift/actions/database-upgrade-scripts diff --git a/config/identical-files.json b/config/identical-files.json index 91056103836..20d7dbc3606 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -94,8 +94,8 @@ "csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll" ], "Model as Data Generation Java/C# - CaptureModels": [ - "java/ql/src/utils/model-generator/internal/CaptureModels.qll", - "csharp/ql/src/utils/model-generator/internal/CaptureModels.qll" + "java/ql/src/utils/modelgenerator/internal/CaptureModels.qll", + "csharp/ql/src/utils/modelgenerator/internal/CaptureModels.qll" ], "Sign Java/C#": [ "java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll", @@ -486,40 +486,6 @@ "python/ql/lib/semmle/python/security/internal/SensitiveDataHeuristics.qll", "ruby/ql/lib/codeql/ruby/security/internal/SensitiveDataHeuristics.qll" ], - "ReDoS Util Python/JS/Ruby/Java": [ - "javascript/ql/lib/semmle/javascript/security/regexp/NfaUtils.qll", - "python/ql/lib/semmle/python/security/regexp/NfaUtils.qll", - "ruby/ql/lib/codeql/ruby/security/regexp/NfaUtils.qll", - "java/ql/lib/semmle/code/java/security/regexp/NfaUtils.qll" - ], - "ReDoS Exponential Python/JS/Ruby/Java": [ - "javascript/ql/lib/semmle/javascript/security/regexp/ExponentialBackTracking.qll", - "python/ql/lib/semmle/python/security/regexp/ExponentialBackTracking.qll", - "ruby/ql/lib/codeql/ruby/security/regexp/ExponentialBackTracking.qll", - "java/ql/lib/semmle/code/java/security/regexp/ExponentialBackTracking.qll" - ], - "ReDoS Polynomial Python/JS/Ruby/Java": [ - "javascript/ql/lib/semmle/javascript/security/regexp/SuperlinearBackTracking.qll", - "python/ql/lib/semmle/python/security/regexp/SuperlinearBackTracking.qll", - "ruby/ql/lib/codeql/ruby/security/regexp/SuperlinearBackTracking.qll", - "java/ql/lib/semmle/code/java/security/regexp/SuperlinearBackTracking.qll" - ], - "RegexpMatching Python/JS/Ruby": [ - "javascript/ql/lib/semmle/javascript/security/regexp/RegexpMatching.qll", - "python/ql/lib/semmle/python/security/regexp/RegexpMatching.qll", - "ruby/ql/lib/codeql/ruby/security/regexp/RegexpMatching.qll" - ], - "BadTagFilterQuery Python/JS/Ruby": [ - "javascript/ql/lib/semmle/javascript/security/BadTagFilterQuery.qll", - "python/ql/lib/semmle/python/security/BadTagFilterQuery.qll", - "ruby/ql/lib/codeql/ruby/security/BadTagFilterQuery.qll" - ], - "OverlyLargeRange Python/JS/Ruby/Java": [ - "javascript/ql/lib/semmle/javascript/security/OverlyLargeRangeQuery.qll", - "python/ql/lib/semmle/python/security/OverlyLargeRangeQuery.qll", - "ruby/ql/lib/codeql/ruby/security/OverlyLargeRangeQuery.qll", - "java/ql/lib/semmle/code/java/security/OverlyLargeRangeQuery.qll" - ], "CFG": [ "csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll", "ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll", diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll index dde16ab5a2a..f681e90aa21 100644 --- a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll @@ -136,6 +136,18 @@ module Consistency { msg = "Local flow step does not preserve enclosing callable." } + query predicate readStepIsLocal(Node n1, Node n2, string msg) { + readStep(n1, _, n2) and + nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and + msg = "Read step does not preserve enclosing callable." + } + + query predicate storeStepIsLocal(Node n1, Node n2, string msg) { + storeStep(n1, _, n2) and + nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and + msg = "Store step does not preserve enclosing callable." + } + private DataFlowType typeRepr() { result = getNodeType(_) } query predicate compatibleTypesReflexive(DataFlowType t, string msg) { diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll index dde16ab5a2a..f681e90aa21 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll @@ -136,6 +136,18 @@ module Consistency { msg = "Local flow step does not preserve enclosing callable." } + query predicate readStepIsLocal(Node n1, Node n2, string msg) { + readStep(n1, _, n2) and + nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and + msg = "Read step does not preserve enclosing callable." + } + + query predicate storeStepIsLocal(Node n1, Node n2, string msg) { + storeStep(n1, _, n2) and + nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and + msg = "Store step does not preserve enclosing callable." + } + private DataFlowType typeRepr() { result = getNodeType(_) } query predicate compatibleTypesReflexive(DataFlowType t, string msg) { diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll index dde16ab5a2a..f681e90aa21 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll @@ -136,6 +136,18 @@ module Consistency { msg = "Local flow step does not preserve enclosing callable." } + query predicate readStepIsLocal(Node n1, Node n2, string msg) { + readStep(n1, _, n2) and + nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and + msg = "Read step does not preserve enclosing callable." + } + + query predicate storeStepIsLocal(Node n1, Node n2, string msg) { + storeStep(n1, _, n2) and + nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and + msg = "Store step does not preserve enclosing callable." + } + private DataFlowType typeRepr() { result = getNodeType(_) } query predicate compatibleTypesReflexive(DataFlowType t, string msg) { diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll index 3d2eda59799..9ac92597b1a 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll @@ -16,21 +16,23 @@ private class StdBasicString extends ClassTemplateInstantiation { } /** - * Additional model for `std::string` constructors that reference the character - * type of the container, or an iterator. For example construction from - * iterators: - * ``` - * std::string b(a.begin(), a.end()); - * ``` + * The `std::basic_string::iterator` declaration. */ -private class StdStringConstructor extends Constructor, TaintFunction { - StdStringConstructor() { this.getDeclaringType() instanceof StdBasicString } +private class StdBasicStringIterator extends Iterator, Type { + StdBasicStringIterator() { + this.getEnclosingElement() instanceof StdBasicString and this.hasName("iterator") + } +} +/** + * A `std::string` function for which taint should be propagated. + */ +abstract private class StdStringTaintFunction extends TaintFunction { /** * Gets the index of a parameter to this function that is a string (or * character). */ - int getAStringParameterIndex() { + final int getAStringParameterIndex() { exists(Type paramType | paramType = this.getParameter(result).getUnspecifiedType() | // e.g. `std::basic_string::CharT *` paramType instanceof PointerType @@ -41,15 +43,28 @@ private class StdStringConstructor extends Constructor, TaintFunction { this.getDeclaringType().getTemplateArgument(2).(Type).getUnspecifiedType() or // i.e. `std::basic_string::CharT` - this.getParameter(result).getUnspecifiedType() = - this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() + paramType = this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() ) } /** * Gets the index of a parameter to this function that is an iterator. */ - int getAnIteratorParameterIndex() { this.getParameter(result).getType() instanceof Iterator } + final int getAnIteratorParameterIndex() { + this.getParameter(result).getType() instanceof Iterator + } +} + +/** + * Additional model for `std::string` constructors that reference the character + * type of the container, or an iterator. For example construction from + * iterators: + * ``` + * std::string b(a.begin(), a.end()); + * ``` + */ +private class StdStringConstructor extends Constructor, StdStringTaintFunction { + StdStringConstructor() { this.getDeclaringType() instanceof StdBasicString } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // taint flow from any parameter of the value type to the returned object @@ -68,7 +83,7 @@ private class StdStringConstructor extends Constructor, TaintFunction { /** * The `std::string` function `c_str`. */ -private class StdStringCStr extends TaintFunction { +private class StdStringCStr extends StdStringTaintFunction { StdStringCStr() { this.getClassAndName("c_str") instanceof StdBasicString } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { @@ -81,7 +96,7 @@ private class StdStringCStr extends TaintFunction { /** * The `std::string` function `data`. */ -private class StdStringData extends TaintFunction { +private class StdStringData extends StdStringTaintFunction { StdStringData() { this.getClassAndName("data") instanceof StdBasicString } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { @@ -99,7 +114,7 @@ private class StdStringData extends TaintFunction { /** * The `std::string` function `push_back`. */ -private class StdStringPush extends TaintFunction { +private class StdStringPush extends StdStringTaintFunction { StdStringPush() { this.getClassAndName("push_back") instanceof StdBasicString } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { @@ -112,7 +127,7 @@ private class StdStringPush extends TaintFunction { /** * The `std::string` functions `front` and `back`. */ -private class StdStringFrontBack extends TaintFunction { +private class StdStringFrontBack extends StdStringTaintFunction { StdStringFrontBack() { this.getClassAndName(["front", "back"]) instanceof StdBasicString } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { @@ -125,7 +140,7 @@ private class StdStringFrontBack extends TaintFunction { /** * The (non-member) `std::string` function `operator+`. */ -private class StdStringPlus extends TaintFunction { +private class StdStringPlus extends StdStringTaintFunction { StdStringPlus() { this.hasQualifiedName(["std", "bsl"], "operator+") and this.getUnspecifiedType() instanceof StdBasicString @@ -142,31 +157,15 @@ private class StdStringPlus extends TaintFunction { } /** - * The `std::string` functions `operator+=`, `append`, `insert` and - * `replace`. All of these functions combine the existing string - * with a new string (or character) from one of the arguments. + * The `std::string` functions `operator+=`, `append` and `replace`. + * All of these functions combine the existing string with a new + * string (or character) from one of the arguments. */ -private class StdStringAppend extends TaintFunction { +private class StdStringAppend extends StdStringTaintFunction { StdStringAppend() { - this.getClassAndName(["operator+=", "append", "insert", "replace"]) instanceof StdBasicString + this.getClassAndName(["operator+=", "append", "replace"]) instanceof StdBasicString } - /** - * Gets the index of a parameter to this function that is a string (or - * character). - */ - int getAStringParameterIndex() { - this.getParameter(result).getType() instanceof PointerType or // e.g. `std::basic_string::CharT *` - this.getParameter(result).getType() instanceof ReferenceType or // e.g. `std::basic_string &` - this.getParameter(result).getUnspecifiedType() = - this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. `std::basic_string::CharT` - } - - /** - * Gets the index of a parameter to this function that is an iterator. - */ - int getAnIteratorParameterIndex() { this.getParameter(result).getType() instanceof Iterator } - override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // flow from string and parameter to string (qualifier) and return value ( @@ -186,28 +185,44 @@ private class StdStringAppend extends TaintFunction { } } +/** + * The `std::string` function `insert`. + */ +private class StdStringInsert extends StdStringTaintFunction { + StdStringInsert() { this.getClassAndName("insert") instanceof StdBasicString } + + /** + * Holds if the return type is an iterator. + */ + predicate hasIteratorReturnValue() { this.getType() instanceof Iterator } + + override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { + // flow from string and parameter to string (qualifier) and return value + ( + input.isQualifierObject() or + input.isParameterDeref(this.getAStringParameterIndex()) or + input.isParameter(this.getAnIteratorParameterIndex()) + ) and + ( + output.isQualifierObject() + or + if this.hasIteratorReturnValue() then output.isReturnValue() else output.isReturnValueDeref() + ) + or + // reverse flow from returned reference to the qualifier (for writes to + // the result) + not this.hasIteratorReturnValue() and + input.isReturnValueDeref() and + output.isQualifierObject() + } +} + /** * The standard function `std::string.assign`. */ -private class StdStringAssign extends TaintFunction { +private class StdStringAssign extends StdStringTaintFunction { StdStringAssign() { this.getClassAndName("assign") instanceof StdBasicString } - /** - * Gets the index of a parameter to this function that is a string (or - * character). - */ - int getAStringParameterIndex() { - this.getParameter(result).getType() instanceof PointerType or // e.g. `std::basic_string::CharT *` - this.getParameter(result).getType() instanceof ReferenceType or // e.g. `std::basic_string &` - this.getParameter(result).getUnspecifiedType() = - this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. `std::basic_string::CharT` - } - - /** - * Gets the index of a parameter to this function that is an iterator. - */ - int getAnIteratorParameterIndex() { this.getParameter(result).getType() instanceof Iterator } - override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // flow from parameter to string itself (qualifier) and return value ( @@ -229,7 +244,7 @@ private class StdStringAssign extends TaintFunction { /** * The standard function `std::string.copy`. */ -private class StdStringCopy extends TaintFunction { +private class StdStringCopy extends StdStringTaintFunction { StdStringCopy() { this.getClassAndName("copy") instanceof StdBasicString } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { @@ -242,7 +257,7 @@ private class StdStringCopy extends TaintFunction { /** * The standard function `std::string.substr`. */ -private class StdStringSubstr extends TaintFunction { +private class StdStringSubstr extends StdStringTaintFunction { StdStringSubstr() { this.getClassAndName("substr") instanceof StdBasicString } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { @@ -255,7 +270,7 @@ private class StdStringSubstr extends TaintFunction { /** * The `std::string` functions `at` and `operator[]`. */ -private class StdStringAt extends TaintFunction { +private class StdStringAt extends StdStringTaintFunction { StdStringAt() { this.getClassAndName(["at", "operator[]"]) instanceof StdBasicString } override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected index 11bb0c21789..8c15a1cbcb9 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected @@ -6,6 +6,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected index cb3f20d9e53..ea2f4ae0d29 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected @@ -21,6 +21,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected b/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected index f66ef23ba74..8a9e15049fc 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected @@ -12,6 +12,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected index 4dee8fb627f..c1b3d0c66b7 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected @@ -15,6 +15,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/bsd.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/bsd.cpp index fe138a8d8fc..0ef3355f1db 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/bsd.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/bsd.cpp @@ -9,7 +9,7 @@ struct sockaddr { char* sa_data; }; -int accept(int, const sockaddr*, int*); +int accept(int, sockaddr*, int*); void sink(sockaddr); @@ -20,5 +20,5 @@ void test_accept() { int a = accept(s, &addr, &size); sink(a); // $ ast=17:11 ir SPURIOUS: ast=18:12 - sink(addr); // $ ast,ir + sink(addr); // $ ast=17:11 ir SPURIOUS: ast=18:12 } diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected index 20f18d62201..1bd40e4e5f1 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -142,9 +142,14 @@ | bsd.cpp:19:14:19:29 | sizeof(sockaddr) | bsd.cpp:20:29:20:32 | size | | | bsd.cpp:20:11:20:16 | call to accept | bsd.cpp:22:8:22:8 | a | | | bsd.cpp:20:18:20:18 | s | bsd.cpp:20:11:20:16 | call to accept | TAINT | +| bsd.cpp:20:18:20:18 | s | bsd.cpp:20:21:20:25 | ref arg & ... | TAINT | | bsd.cpp:20:21:20:25 | & ... | bsd.cpp:20:11:20:16 | call to accept | TAINT | +| bsd.cpp:20:21:20:25 | & ... | bsd.cpp:20:21:20:25 | ref arg & ... | TAINT | +| bsd.cpp:20:21:20:25 | ref arg & ... | bsd.cpp:20:22:20:25 | addr [inner post update] | | +| bsd.cpp:20:21:20:25 | ref arg & ... | bsd.cpp:23:8:23:11 | addr | | | bsd.cpp:20:22:20:25 | addr | bsd.cpp:20:11:20:16 | call to accept | TAINT | | bsd.cpp:20:22:20:25 | addr | bsd.cpp:20:21:20:25 | & ... | | +| bsd.cpp:20:22:20:25 | addr | bsd.cpp:20:21:20:25 | ref arg & ... | TAINT | | bsd.cpp:20:28:20:32 | ref arg & ... | bsd.cpp:20:29:20:32 | size [inner post update] | | | bsd.cpp:20:29:20:32 | size | bsd.cpp:20:28:20:32 | & ... | | | constructor_delegation.cpp:8:2:8:8 | this | constructor_delegation.cpp:8:20:8:24 | constructor init of field x [pre-this] | | diff --git a/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected index 086fa0415b7..1980b113311 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected @@ -52,6 +52,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected index 70f8446ec5a..26abe41c33f 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected @@ -1463,6 +1463,8 @@ uniqueNodeToString missingToString parameterCallable localFlowIsLocal +readStepIsLocal +storeStepIsLocal compatibleTypesReflexive unreachableNodeCCtx localCallNodes diff --git a/csharp/documentation/library-coverage/coverage.csv b/csharp/documentation/library-coverage/coverage.csv index 6ab77f6aba6..5d0813f71e9 100644 --- a/csharp/documentation/library-coverage/coverage.csv +++ b/csharp/documentation/library-coverage/coverage.csv @@ -24,5 +24,5 @@ Microsoft.Win32,,,8,,,,,,,,,,,,8, MySql.Data.MySqlClient,48,,,,,,,,,,48,,,,, Newtonsoft.Json,,,91,,,,,,,,,,,,73,18 ServiceStack,194,,7,27,,,,,,75,92,,,,7, -System,65,4,12131,,8,8,9,,4,,33,3,1,3,10139,1992 +System,65,4,12142,,8,8,9,,4,,33,3,1,3,10151,1991 Windows.Security.Cryptography.Core,1,,,,,,,1,,,,,,,, diff --git a/csharp/documentation/library-coverage/coverage.rst b/csharp/documentation/library-coverage/coverage.rst index 0f3dce2941a..fa178752fe3 100644 --- a/csharp/documentation/library-coverage/coverage.rst +++ b/csharp/documentation/library-coverage/coverage.rst @@ -8,7 +8,7 @@ C# framework & library support Framework / library,Package,Flow sources,Taint & value steps,Sinks (total),`CWE-079` :sub:`Cross-site scripting` `ServiceStack `_,"``ServiceStack.*``, ``ServiceStack``",,7,194, - System,"``System.*``, ``System``",4,12131,65,7 + System,"``System.*``, ``System``",4,12142,65,7 Others,"``Dapper``, ``JsonToItemsTaskFactory``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.CSharp``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Caching.Distributed``, ``Microsoft.Extensions.Caching.Memory``, ``Microsoft.Extensions.Configuration``, ``Microsoft.Extensions.DependencyInjection``, ``Microsoft.Extensions.DependencyModel``, ``Microsoft.Extensions.FileProviders``, ``Microsoft.Extensions.FileSystemGlobbing``, ``Microsoft.Extensions.Hosting``, ``Microsoft.Extensions.Http``, ``Microsoft.Extensions.Logging``, ``Microsoft.Extensions.Options``, ``Microsoft.Extensions.Primitives``, ``Microsoft.Interop``, ``Microsoft.NET.Build.Tasks``, ``Microsoft.NETCore.Platforms.BuildTasks``, ``Microsoft.VisualBasic``, ``Microsoft.Win32``, ``MySql.Data.MySqlClient``, ``Newtonsoft.Json``, ``Windows.Security.Cryptography.Core``",,556,138, - Totals,,4,12694,397,7 + Totals,,4,12705,397,7 diff --git a/csharp/ql/consistency-queries/SsaConsistency.ql b/csharp/ql/consistency-queries/SsaConsistency.ql index 71f88bf2ab0..bd666a71e49 100644 --- a/csharp/ql/consistency-queries/SsaConsistency.ql +++ b/csharp/ql/consistency-queries/SsaConsistency.ql @@ -1,8 +1,8 @@ import csharp -import semmle.code.csharp.dataflow.internal.SsaImpl::Consistency as Consistency +import semmle.code.csharp.dataflow.internal.SsaImpl::Consistency import Ssa -class MyRelevantDefinition extends Consistency::RelevantDefinition, Ssa::Definition { +class MyRelevantDefinition extends RelevantDefinition, Ssa::Definition { override predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn ) { @@ -10,14 +10,6 @@ class MyRelevantDefinition extends Consistency::RelevantDefinition, Ssa::Definit } } -query predicate nonUniqueDef = Consistency::nonUniqueDef/4; - -query predicate readWithoutDef = Consistency::readWithoutDef/3; - -query predicate deadDef = Consistency::deadDef/2; - -query predicate notDominatedByDef = Consistency::notDominatedByDef/4; - query predicate localDeclWithSsaDef(LocalVariableDeclExpr d) { // Local variables in C# must be initialized before every use, so uninitialized // local variables should not have an SSA definition, as that would imply that diff --git a/csharp/ql/lib/semmle/code/csharp/Type.qll b/csharp/ql/lib/semmle/code/csharp/Type.qll index f95ded84771..dfabea580a4 100644 --- a/csharp/ql/lib/semmle/code/csharp/Type.qll +++ b/csharp/ql/lib/semmle/code/csharp/Type.qll @@ -824,6 +824,8 @@ class RecordClass extends RecordType, Class { */ class AnonymousClass extends Class { AnonymousClass() { anonymous_types(this) } + + override string getAPrimaryQlClass() { result = "AnonymousClass" } } /** diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll index dde16ab5a2a..f681e90aa21 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll @@ -136,6 +136,18 @@ module Consistency { msg = "Local flow step does not preserve enclosing callable." } + query predicate readStepIsLocal(Node n1, Node n2, string msg) { + readStep(n1, _, n2) and + nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and + msg = "Read step does not preserve enclosing callable." + } + + query predicate storeStepIsLocal(Node n1, Node n2, string msg) { + storeStep(n1, _, n2) and + nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and + msg = "Store step does not preserve enclosing callable." + } + private DataFlowType typeRepr() { result = getNodeType(_) } query predicate compatibleTypesReflexive(DataFlowType t, string msg) { diff --git a/csharp/ql/src/Security Features/CWE-114/AssemblyPathInjection.ql b/csharp/ql/src/Security Features/CWE-114/AssemblyPathInjection.ql index 3ca894db15a..3705ece0e72 100644 --- a/csharp/ql/src/Security Features/CWE-114/AssemblyPathInjection.ql +++ b/csharp/ql/src/Security Features/CWE-114/AssemblyPathInjection.ql @@ -3,7 +3,7 @@ * @description Loading a .NET assembly based on a path constructed from user-controlled sources * may allow a malicious user to load code which modifies the program in unintended * ways. - * @kind problem + * @kind path-problem * @id cs/assembly-path-injection * @problem.severity error * @security-severity 8.2 @@ -15,6 +15,7 @@ import csharp import semmle.code.csharp.security.dataflow.flowsources.Remote import semmle.code.csharp.commons.Util +import DataFlow::PathGraph /** * A taint-tracking configuration for untrusted user input used to load a DLL. @@ -47,6 +48,7 @@ class TaintTrackingConfiguration extends TaintTracking::Configuration { } } -from TaintTrackingConfiguration c, DataFlow::Node source, DataFlow::Node sink -where c.hasFlow(source, sink) -select sink, "This assembly path depends on a $@.", source, "user-provided value" +from TaintTrackingConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink +where c.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "This assembly path depends on a $@.", source, + "user-provided value" diff --git a/csharp/ql/src/Security Features/CWE-321/HardcodedEncryptionKey.ql b/csharp/ql/src/Security Features/CWE-321/HardcodedEncryptionKey.ql index 8f30847cde0..8e516f44d4a 100644 --- a/csharp/ql/src/Security Features/CWE-321/HardcodedEncryptionKey.ql +++ b/csharp/ql/src/Security Features/CWE-321/HardcodedEncryptionKey.ql @@ -1,7 +1,7 @@ /** * @name Hard-coded encryption key * @description The .Key property or rgbKey parameter of a SymmetricAlgorithm should never be a hard-coded value. - * @kind problem + * @kind path-problem * @id cs/hardcoded-key * @problem.severity error * @security-severity 8.1 @@ -15,6 +15,7 @@ import csharp import semmle.code.csharp.security.cryptography.EncryptionKeyDataFlowQuery +import DataFlow::PathGraph /** * The creation of a literal byte array. @@ -36,7 +37,13 @@ class StringLiteralSource extends KeySource { StringLiteralSource() { this.asExpr() instanceof StringLiteral } } -from SymmetricKeyTaintTrackingConfiguration keyFlow, KeySource src, SymmetricEncryptionKeySink sink -where keyFlow.hasFlow(src, sink) -select sink, "This hard-coded $@ is used in symmetric algorithm in " + sink.getDescription(), src, +from + SymmetricKeyTaintTrackingConfiguration keyFlow, DataFlow::PathNode source, + DataFlow::PathNode sink, KeySource srcNode, SymmetricEncryptionKeySink sinkNode +where + keyFlow.hasFlowPath(source, sink) and + source.getNode() = srcNode and + sink.getNode() = sinkNode +select sink.getNode(), source, sink, + "This hard-coded $@ is used in symmetric algorithm in " + sinkNode.getDescription(), srcNode, "symmetric key" diff --git a/csharp/ql/src/Telemetry/ExternalApi.qll b/csharp/ql/src/Telemetry/ExternalApi.qll index d4cc0ae43cc..653eb419b7f 100644 --- a/csharp/ql/src/Telemetry/ExternalApi.qll +++ b/csharp/ql/src/Telemetry/ExternalApi.qll @@ -28,11 +28,16 @@ class TestLibrary extends RefType { * An external API from either the C# Standard Library or a 3rd party library. */ class ExternalApi extends DotNet::Callable { - ExternalApi() { this.isUnboundDeclaration() and this.fromLibrary() } + ExternalApi() { + this.isUnboundDeclaration() and + this.fromLibrary() and + this.(Modifiable).isEffectivelyPublic() + } /** * Gets the unbound type, name and parameter types of this API. */ + bindingset[this] private string getSignature() { result = this.getDeclaringType().getUnboundDeclaration() + "." + this.getName() + "(" + @@ -42,41 +47,33 @@ class ExternalApi extends DotNet::Callable { /** * Gets the namespace of this API. */ - private string getNamespace() { this.getDeclaringType().hasQualifiedName(result, _) } + bindingset[this] + string getNamespace() { this.getDeclaringType().hasQualifiedName(result, _) } /** - * Gets the assembly file name containing this API. + * Gets the namespace and signature of this API. */ - private string getAssembly() { result = this.getFile().getBaseName() } - - /** - * Gets the assembly file name and namespace of this API. - */ - string getInfoPrefix() { result = this.getAssembly() + "#" + this.getNamespace() } - - /** - * Gets the assembly file name, namespace and signature of this API. - */ - string getInfo() { result = this.getInfoPrefix() + "#" + this.getSignature() } - - /** Gets a call to this API callable. */ - DispatchCall getACall() { - this = result.getADynamicTarget().getUnboundDeclaration() - or - this = result.getAStaticTarget().getUnboundDeclaration() - } + bindingset[this] + string getApiName() { result = this.getNamespace() + "#" + this.getSignature() } /** Gets a node that is an input to a call to this API. */ private ArgumentNode getAnInput() { - result.getCall().(DataFlowDispatch::NonDelegateDataFlowCall).getDispatchCall() = this.getACall() + result + .getCall() + .(DataFlowDispatch::NonDelegateDataFlowCall) + .getATarget(_) + .getUnboundDeclaration() = this } /** Gets a node that is an output from a call to this API. */ private DataFlow::Node getAnOutput() { - exists(DataFlowDispatch::NonDelegateDataFlowCall call, DataFlowImplCommon::ReturnKindExt ret | - result = ret.getAnOutNode(call) + exists( + Call c, DataFlowDispatch::NonDelegateDataFlowCall dc, DataFlowImplCommon::ReturnKindExt ret | - this.getACall() = call.getDispatchCall() + dc.getDispatchCall().getCall() = c and + c.getTarget().getUnboundDeclaration() = this + | + result = ret.getAnOutNode(dc) ) } @@ -125,30 +122,30 @@ signature predicate relevantApi(ExternalApi api); * for restricting the number or returned results based on a certain limit. */ module Results { - private int getUsages(string apiInfo) { + private int getUsages(string apiName) { result = - strictcount(DispatchCall c, ExternalApi api | - c = api.getACall() and - apiInfo = api.getInfo() and + strictcount(Call c, ExternalApi api | + c.getTarget().getUnboundDeclaration() = api and + apiName = api.getApiName() and getRelevantUsages(api) ) } - private int getOrder(string apiInfo) { - apiInfo = - rank[result](string info, int usages | - usages = getUsages(info) + private int getOrder(string apiName) { + apiName = + rank[result](string name, int usages | + usages = getUsages(name) | - info order by usages desc, info + name order by usages desc, name ) } /** - * Holds if there exists an API with `apiInfo` that is being used `usages` times + * Holds if there exists an API with `apiName` that is being used `usages` times * and if it is in the top results (guarded by resultLimit). */ - predicate restrict(string apiInfo, int usages) { - usages = getUsages(apiInfo) and - getOrder(apiInfo) <= resultLimit() + predicate restrict(string apiName, int usages) { + usages = getUsages(apiName) and + getOrder(apiName) <= resultLimit() } } diff --git a/csharp/ql/src/Telemetry/ExternalLibraryUsage.ql b/csharp/ql/src/Telemetry/ExternalLibraryUsage.ql index dfc1a8e062c..f06a18054dc 100644 --- a/csharp/ql/src/Telemetry/ExternalLibraryUsage.ql +++ b/csharp/ql/src/Telemetry/ExternalLibraryUsage.ql @@ -1,6 +1,6 @@ /** * @name External libraries - * @description A list of external libraries used in the code + * @description A list of external libraries used in the code given by their namespace. * @kind metric * @tags summary telemetry * @id csharp/telemetry/external-libs @@ -10,23 +10,23 @@ private import csharp private import semmle.code.csharp.dispatch.Dispatch private import ExternalApi -private predicate getRelevantUsages(string info, int usages) { +private predicate getRelevantUsages(string namespace, int usages) { usages = - strictcount(DispatchCall c, ExternalApi api | - c = api.getACall() and - api.getInfoPrefix() = info and + strictcount(Call c, ExternalApi api | + c.getTarget().getUnboundDeclaration() = api and + api.getNamespace() = namespace and not api.isUninteresting() ) } -private int getOrder(string info) { - info = +private int getOrder(string namespace) { + namespace = rank[result](string i, int usages | getRelevantUsages(i, usages) | i order by usages desc, i) } -from ExternalApi api, string info, int usages +from ExternalApi api, string namespace, int usages where - info = api.getInfoPrefix() and - getRelevantUsages(info, usages) and - getOrder(info) <= resultLimit() -select info, usages order by usages desc + namespace = api.getNamespace() and + getRelevantUsages(namespace, usages) and + getOrder(namespace) <= resultLimit() +select namespace, usages order by usages desc diff --git a/csharp/ql/src/meta/frameworks/UnsupportedExternalAPIs.ql b/csharp/ql/src/meta/frameworks/UnsupportedExternalAPIs.ql index b7e2d1241a0..4eef46ef86b 100644 --- a/csharp/ql/src/meta/frameworks/UnsupportedExternalAPIs.ql +++ b/csharp/ql/src/meta/frameworks/UnsupportedExternalAPIs.ql @@ -13,9 +13,9 @@ private import semmle.code.csharp.dispatch.Dispatch private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl private import Telemetry.ExternalApi -from DispatchCall c, ExternalApi api +from Call c, ExternalApi api where - c = api.getACall() and + c.getTarget().getUnboundDeclaration() = api and not api.isUninteresting() and not api.isSupported() and not api instanceof FlowSummaryImpl::Public::NegativeSummarizedCallable diff --git a/csharp/ql/src/utils/model-generator/CaptureDiscardedSummaryModels.ql b/csharp/ql/src/utils/model-generator/CaptureDiscardedSummaryModels.ql index b9e0cc23987..f8529caaba0 100644 --- a/csharp/ql/src/utils/model-generator/CaptureDiscardedSummaryModels.ql +++ b/csharp/ql/src/utils/model-generator/CaptureDiscardedSummaryModels.ql @@ -5,8 +5,8 @@ */ import semmle.code.csharp.dataflow.ExternalFlow -import internal.CaptureModels -import internal.CaptureSummaryFlow +import utils.modelgenerator.internal.CaptureModels +import utils.modelgenerator.internal.CaptureSummaryFlow from DataFlowTargetApi api, string flow where flow = captureFlow(api) and hasSummary(api, false) diff --git a/csharp/ql/src/utils/model-generator/CaptureNegativeSummaryModels.ql b/csharp/ql/src/utils/model-generator/CaptureNegativeSummaryModels.ql index 0e26d3eee7f..9188b54ef40 100644 --- a/csharp/ql/src/utils/model-generator/CaptureNegativeSummaryModels.ql +++ b/csharp/ql/src/utils/model-generator/CaptureNegativeSummaryModels.ql @@ -7,8 +7,8 @@ */ import semmle.code.csharp.dataflow.ExternalFlow -import internal.CaptureModels -import internal.CaptureSummaryFlow +import utils.modelgenerator.internal.CaptureModels +import utils.modelgenerator.internal.CaptureSummaryFlow from DataFlowTargetApi api, string noflow where diff --git a/csharp/ql/src/utils/model-generator/CaptureSinkModels.ql b/csharp/ql/src/utils/model-generator/CaptureSinkModels.ql index 04942aa36cc..68b3ec91974 100644 --- a/csharp/ql/src/utils/model-generator/CaptureSinkModels.ql +++ b/csharp/ql/src/utils/model-generator/CaptureSinkModels.ql @@ -6,7 +6,7 @@ * @tags model-generator */ -import internal.CaptureModels +import utils.modelgenerator.internal.CaptureModels class Activate extends ActiveConfiguration { override predicate activateToSinkConfig() { any() } diff --git a/csharp/ql/src/utils/model-generator/CaptureSourceModels.ql b/csharp/ql/src/utils/model-generator/CaptureSourceModels.ql index 65ea96c3003..9c3c87d8251 100644 --- a/csharp/ql/src/utils/model-generator/CaptureSourceModels.ql +++ b/csharp/ql/src/utils/model-generator/CaptureSourceModels.ql @@ -6,7 +6,7 @@ * @tags model-generator */ -import internal.CaptureModels +import utils.modelgenerator.internal.CaptureModels class Activate extends ActiveConfiguration { override predicate activateFromSourceConfig() { any() } diff --git a/csharp/ql/src/utils/model-generator/CaptureSummaryModels.ql b/csharp/ql/src/utils/model-generator/CaptureSummaryModels.ql index f308c57c89d..0dd22e3babb 100644 --- a/csharp/ql/src/utils/model-generator/CaptureSummaryModels.ql +++ b/csharp/ql/src/utils/model-generator/CaptureSummaryModels.ql @@ -7,8 +7,8 @@ */ import semmle.code.csharp.dataflow.ExternalFlow -import internal.CaptureModels -import internal.CaptureSummaryFlow +import utils.modelgenerator.internal.CaptureModels +import utils.modelgenerator.internal.CaptureSummaryFlow from DataFlowTargetApi api, string flow where flow = captureFlow(api) and not hasSummary(api, false) diff --git a/csharp/ql/src/utils/model-generator/CaptureTypeBasedSummaryModels.ql b/csharp/ql/src/utils/model-generator/CaptureTypeBasedSummaryModels.ql index e3dcc9dc2f6..7cd23988321 100644 --- a/csharp/ql/src/utils/model-generator/CaptureTypeBasedSummaryModels.ql +++ b/csharp/ql/src/utils/model-generator/CaptureTypeBasedSummaryModels.ql @@ -7,7 +7,7 @@ */ import semmle.code.csharp.dataflow.ExternalFlow -import internal.CaptureTypeBasedSummaryModels +import utils.modelgenerator.internal.CaptureTypeBasedSummaryModels from TypeBasedFlowTargetApi api, string flow where flow = captureFlow(api) diff --git a/csharp/ql/src/utils/model-generator/internal/CaptureModels.qll b/csharp/ql/src/utils/modelgenerator/internal/CaptureModels.qll similarity index 100% rename from csharp/ql/src/utils/model-generator/internal/CaptureModels.qll rename to csharp/ql/src/utils/modelgenerator/internal/CaptureModels.qll diff --git a/csharp/ql/src/utils/model-generator/internal/CaptureModelsSpecific.qll b/csharp/ql/src/utils/modelgenerator/internal/CaptureModelsSpecific.qll similarity index 100% rename from csharp/ql/src/utils/model-generator/internal/CaptureModelsSpecific.qll rename to csharp/ql/src/utils/modelgenerator/internal/CaptureModelsSpecific.qll diff --git a/csharp/ql/src/utils/model-generator/internal/CaptureSummaryFlow.qll b/csharp/ql/src/utils/modelgenerator/internal/CaptureSummaryFlow.qll similarity index 100% rename from csharp/ql/src/utils/model-generator/internal/CaptureSummaryFlow.qll rename to csharp/ql/src/utils/modelgenerator/internal/CaptureSummaryFlow.qll diff --git a/csharp/ql/src/utils/model-generator/internal/CaptureTypeBasedSummaryModels.qll b/csharp/ql/src/utils/modelgenerator/internal/CaptureTypeBasedSummaryModels.qll similarity index 98% rename from csharp/ql/src/utils/model-generator/internal/CaptureTypeBasedSummaryModels.qll rename to csharp/ql/src/utils/modelgenerator/internal/CaptureTypeBasedSummaryModels.qll index 02f00986c3c..de8a80b96f9 100644 --- a/csharp/ql/src/utils/model-generator/internal/CaptureTypeBasedSummaryModels.qll +++ b/csharp/ql/src/utils/modelgenerator/internal/CaptureTypeBasedSummaryModels.qll @@ -64,6 +64,7 @@ private string getSyntheticField(TypeParameter tp) { */ private string implicit(DotNet::Callable callable, TypeParameter tp) { classTypeParameter(callable, tp) and + not callable.(Modifiable).isStatic() and exists(string access | if genericCollectionType(callable.getDeclaringType(), tp) then access = ".Element" @@ -188,7 +189,7 @@ class TypeBasedFlowTargetApi extends Specific::TargetApiSpecific { * Gets the string representation of all type based summaries for `this` * inspired by the Theorems for Free approach. * - * Examples could be (see C# psuedo code below) + * Examples could be (see C# pseudo code below) * (1) `Get` returns a value of type `T`. We assume that the returned * value was fetched from a (synthetic) field. * (2) `Set` consumes a value of type `T`. We assume that the value is stored in diff --git a/csharp/ql/test/query-tests/Security Features/CWE-114/AssemblyPathInjection/AssemblyPathInjection.expected b/csharp/ql/test/query-tests/Security Features/CWE-114/AssemblyPathInjection/AssemblyPathInjection.expected index 2f3c3bbb4a6..396d766c3e6 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-114/AssemblyPathInjection/AssemblyPathInjection.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-114/AssemblyPathInjection/AssemblyPathInjection.expected @@ -1 +1,11 @@ -| Test.cs:10:36:10:46 | access to local variable libraryName | This assembly path depends on a $@. | Test.cs:7:26:7:48 | access to property QueryString | user-provided value | +edges +| Test.cs:7:26:7:48 | access to property QueryString : NameValueCollection | Test.cs:7:26:7:63 | access to indexer : String | +| Test.cs:7:26:7:48 | access to property QueryString : NameValueCollection | Test.cs:10:36:10:46 | access to local variable libraryName | +| Test.cs:7:26:7:63 | access to indexer : String | Test.cs:10:36:10:46 | access to local variable libraryName | +nodes +| Test.cs:7:26:7:48 | access to property QueryString : NameValueCollection | semmle.label | access to property QueryString : NameValueCollection | +| Test.cs:7:26:7:63 | access to indexer : String | semmle.label | access to indexer : String | +| Test.cs:10:36:10:46 | access to local variable libraryName | semmle.label | access to local variable libraryName | +subpaths +#select +| Test.cs:10:36:10:46 | access to local variable libraryName | Test.cs:7:26:7:48 | access to property QueryString : NameValueCollection | Test.cs:10:36:10:46 | access to local variable libraryName | This assembly path depends on a $@. | Test.cs:7:26:7:48 | access to property QueryString : NameValueCollection | user-provided value | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.expected b/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.expected index 7c3a2a7339c..10cb82ff67e 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.expected @@ -1,7 +1,40 @@ -| HardcodedSymmetricEncryptionKey.cs:17:21:17:97 | array creation of type Byte[] | This hard-coded $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:17:21:17:97 | array creation of type Byte[] | symmetric key | -| HardcodedSymmetricEncryptionKey.cs:22:23:22:99 | array creation of type Byte[] | This hard-coded $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:22:23:22:99 | array creation of type Byte[] | symmetric key | -| HardcodedSymmetricEncryptionKey.cs:31:21:31:21 | access to local variable d | This hard-coded $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | symmetric key | -| HardcodedSymmetricEncryptionKey.cs:68:87:68:94 | access to parameter password | This hard-coded $@ is used in symmetric algorithm in Decryptor(rgbKey, IV) | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | symmetric key | -| HardcodedSymmetricEncryptionKey.cs:108:23:108:25 | access to parameter key | This hard-coded $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | symmetric key | -| HardcodedSymmetricEncryptionKey.cs:121:87:121:89 | access to parameter key | This hard-coded $@ is used in symmetric algorithm in Encryptor(rgbKey, IV) | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | symmetric key | -| HardcodedSymmetricEncryptionKey.cs:121:87:121:89 | access to parameter key | This hard-coded $@ is used in symmetric algorithm in Encryptor(rgbKey, IV) | HardcodedSymmetricEncryptionKey.cs:28:62:28:115 | "Hello, world: here is a very bad way to create a key" | symmetric key | +edges +| HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] : Byte[] | HardcodedSymmetricEncryptionKey.cs:31:21:31:21 | access to local variable d | +| HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] : Byte[] | HardcodedSymmetricEncryptionKey.cs:36:37:36:37 | access to local variable d : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] : Byte[] | HardcodedSymmetricEncryptionKey.cs:41:50:41:50 | access to local variable c : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] : Byte[] | HardcodedSymmetricEncryptionKey.cs:50:35:50:35 | access to local variable c : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:28:39:28:116 | call to method GetBytes : Byte[] | HardcodedSymmetricEncryptionKey.cs:44:51:44:69 | access to local variable byteArrayFromString : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:28:62:28:115 | "Hello, world: here is a very bad way to create a key" : String | HardcodedSymmetricEncryptionKey.cs:28:39:28:116 | call to method GetBytes : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:36:37:36:37 | access to local variable d : Byte[] | HardcodedSymmetricEncryptionKey.cs:103:57:103:59 | key : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:41:50:41:50 | access to local variable c : Byte[] | HardcodedSymmetricEncryptionKey.cs:112:63:112:65 | key : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:44:51:44:69 | access to local variable byteArrayFromString : Byte[] | HardcodedSymmetricEncryptionKey.cs:112:63:112:65 | key : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:50:35:50:35 | access to local variable c : Byte[] | HardcodedSymmetricEncryptionKey.cs:59:64:59:71 | password : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:59:64:59:71 | password : Byte[] | HardcodedSymmetricEncryptionKey.cs:68:87:68:94 | access to parameter password | +| HardcodedSymmetricEncryptionKey.cs:103:57:103:59 | key : Byte[] | HardcodedSymmetricEncryptionKey.cs:108:23:108:25 | access to parameter key | +| HardcodedSymmetricEncryptionKey.cs:112:63:112:65 | key : Byte[] | HardcodedSymmetricEncryptionKey.cs:121:87:121:89 | access to parameter key | +nodes +| HardcodedSymmetricEncryptionKey.cs:17:21:17:97 | array creation of type Byte[] | semmle.label | array creation of type Byte[] | +| HardcodedSymmetricEncryptionKey.cs:22:23:22:99 | array creation of type Byte[] | semmle.label | array creation of type Byte[] | +| HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] : Byte[] | semmle.label | array creation of type Byte[] : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:28:39:28:116 | call to method GetBytes : Byte[] | semmle.label | call to method GetBytes : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:28:62:28:115 | "Hello, world: here is a very bad way to create a key" : String | semmle.label | "Hello, world: here is a very bad way to create a key" : String | +| HardcodedSymmetricEncryptionKey.cs:31:21:31:21 | access to local variable d | semmle.label | access to local variable d | +| HardcodedSymmetricEncryptionKey.cs:36:37:36:37 | access to local variable d : Byte[] | semmle.label | access to local variable d : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:41:50:41:50 | access to local variable c : Byte[] | semmle.label | access to local variable c : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:44:51:44:69 | access to local variable byteArrayFromString : Byte[] | semmle.label | access to local variable byteArrayFromString : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:50:35:50:35 | access to local variable c : Byte[] | semmle.label | access to local variable c : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:59:64:59:71 | password : Byte[] | semmle.label | password : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:68:87:68:94 | access to parameter password | semmle.label | access to parameter password | +| HardcodedSymmetricEncryptionKey.cs:103:57:103:59 | key : Byte[] | semmle.label | key : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:108:23:108:25 | access to parameter key | semmle.label | access to parameter key | +| HardcodedSymmetricEncryptionKey.cs:112:63:112:65 | key : Byte[] | semmle.label | key : Byte[] | +| HardcodedSymmetricEncryptionKey.cs:121:87:121:89 | access to parameter key | semmle.label | access to parameter key | +subpaths +#select +| HardcodedSymmetricEncryptionKey.cs:17:21:17:97 | array creation of type Byte[] | HardcodedSymmetricEncryptionKey.cs:17:21:17:97 | array creation of type Byte[] | HardcodedSymmetricEncryptionKey.cs:17:21:17:97 | array creation of type Byte[] | This hard-coded $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:17:21:17:97 | array creation of type Byte[] | symmetric key | +| HardcodedSymmetricEncryptionKey.cs:22:23:22:99 | array creation of type Byte[] | HardcodedSymmetricEncryptionKey.cs:22:23:22:99 | array creation of type Byte[] | HardcodedSymmetricEncryptionKey.cs:22:23:22:99 | array creation of type Byte[] | This hard-coded $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:22:23:22:99 | array creation of type Byte[] | symmetric key | +| HardcodedSymmetricEncryptionKey.cs:31:21:31:21 | access to local variable d | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] : Byte[] | HardcodedSymmetricEncryptionKey.cs:31:21:31:21 | access to local variable d | This hard-coded $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | symmetric key | +| HardcodedSymmetricEncryptionKey.cs:68:87:68:94 | access to parameter password | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] : Byte[] | HardcodedSymmetricEncryptionKey.cs:68:87:68:94 | access to parameter password | This hard-coded $@ is used in symmetric algorithm in Decryptor(rgbKey, IV) | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | symmetric key | +| HardcodedSymmetricEncryptionKey.cs:108:23:108:25 | access to parameter key | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] : Byte[] | HardcodedSymmetricEncryptionKey.cs:108:23:108:25 | access to parameter key | This hard-coded $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | symmetric key | +| HardcodedSymmetricEncryptionKey.cs:121:87:121:89 | access to parameter key | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] : Byte[] | HardcodedSymmetricEncryptionKey.cs:121:87:121:89 | access to parameter key | This hard-coded $@ is used in symmetric algorithm in Encryptor(rgbKey, IV) | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | symmetric key | +| HardcodedSymmetricEncryptionKey.cs:121:87:121:89 | access to parameter key | HardcodedSymmetricEncryptionKey.cs:28:62:28:115 | "Hello, world: here is a very bad way to create a key" : String | HardcodedSymmetricEncryptionKey.cs:121:87:121:89 | access to parameter key | This hard-coded $@ is used in symmetric algorithm in Encryptor(rgbKey, IV) | HardcodedSymmetricEncryptionKey.cs:28:62:28:115 | "Hello, world: here is a very bad way to create a key" | symmetric key | diff --git a/csharp/ql/test/query-tests/Telemetry/LibraryUsage/ExternalLibraryUsage.expected b/csharp/ql/test/query-tests/Telemetry/LibraryUsage/ExternalLibraryUsage.expected index e64b827f137..7b5c4c57661 100644 --- a/csharp/ql/test/query-tests/Telemetry/LibraryUsage/ExternalLibraryUsage.expected +++ b/csharp/ql/test/query-tests/Telemetry/LibraryUsage/ExternalLibraryUsage.expected @@ -1,2 +1,2 @@ -| System.Private.CoreLib.dll#System | 5 | -| System.Private.CoreLib.dll#System.Collections.Generic | 2 | +| System | 5 | +| System.Collections.Generic | 2 | diff --git a/csharp/ql/test/query-tests/Telemetry/LibraryUsage/SupportedExternalTaint.expected b/csharp/ql/test/query-tests/Telemetry/LibraryUsage/SupportedExternalTaint.expected index e9be925c286..b386e4acb38 100644 --- a/csharp/ql/test/query-tests/Telemetry/LibraryUsage/SupportedExternalTaint.expected +++ b/csharp/ql/test/query-tests/Telemetry/LibraryUsage/SupportedExternalTaint.expected @@ -1 +1 @@ -| System.Private.CoreLib.dll#System.Collections.Generic#List<>.Add(T) | 2 | +| System.Collections.Generic#List<>.Add(T) | 2 | diff --git a/csharp/ql/test/query-tests/Telemetry/SupportedExternalSinks/SupportedExternalSinks.expected b/csharp/ql/test/query-tests/Telemetry/SupportedExternalSinks/SupportedExternalSinks.expected index b85e33ddae7..b3acebc02e0 100644 --- a/csharp/ql/test/query-tests/Telemetry/SupportedExternalSinks/SupportedExternalSinks.expected +++ b/csharp/ql/test/query-tests/Telemetry/SupportedExternalSinks/SupportedExternalSinks.expected @@ -1,2 +1,2 @@ -| System.Web.cs#System.Web#HttpResponse.Write(System.Object) | 2 | -| System.Web.cs#System.Web#HttpResponse.WriteFile(System.String) | 1 | +| System.Web#HttpResponse.Write(System.Object) | 2 | +| System.Web#HttpResponse.WriteFile(System.String) | 1 | diff --git a/csharp/ql/test/query-tests/Telemetry/SupportedExternalSources/SupportedExternalSources.expected b/csharp/ql/test/query-tests/Telemetry/SupportedExternalSources/SupportedExternalSources.expected index 0f125b04b94..93c23f159a6 100644 --- a/csharp/ql/test/query-tests/Telemetry/SupportedExternalSources/SupportedExternalSources.expected +++ b/csharp/ql/test/query-tests/Telemetry/SupportedExternalSources/SupportedExternalSources.expected @@ -1,2 +1,2 @@ -| System.Console.dll#System#Console.ReadLine() | 2 | -| System.Console.dll#System#Console.Read() | 1 | +| System#Console.ReadLine() | 2 | +| System#Console.Read() | 1 | diff --git a/docs/codeql/support/reusables/versions-compilers.rst b/docs/codeql/support/reusables/versions-compilers.rst index 797c9980e0a..d84757c81d7 100644 --- a/docs/codeql/support/reusables/versions-compilers.rst +++ b/docs/codeql/support/reusables/versions-compilers.rst @@ -20,10 +20,11 @@ Java,"Java 7 to 18 [4]_","javac (OpenJDK and Oracle JDK), Eclipse compiler for Java (ECJ) [5]_",``.java`` - JavaScript,ECMAScript 2022 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhtm``, ``.xhtml``, ``.vue``, ``.hbs``, ``.ejs``, ``.njk``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [6]_" - Python [7]_,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10",Not applicable,``.py`` - Ruby [8]_,"up to 3.1",Not applicable,"``.rb``, ``.erb``, ``.gemspec``, ``Gemfile``" - TypeScript [9]_,"2.6-4.8",Standard TypeScript compiler,"``.ts``, ``.tsx``, ``.mts``, ``.cts``" + Kotlin [6]_,"Kotlin 1.5.0 to 1.7.21","kotlinc",``.kt`` + JavaScript,ECMAScript 2022 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhtm``, ``.xhtml``, ``.vue``, ``.hbs``, ``.ejs``, ``.njk``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [7]_" + Python [8]_,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10",Not applicable,``.py`` + Ruby [9]_,"up to 3.1",Not applicable,"``.rb``, ``.erb``, ``.gemspec``, ``Gemfile``" + TypeScript [10]_,"2.6-4.8",Standard TypeScript compiler,"``.ts``, ``.tsx``, ``.mts``, ``.cts``" .. container:: footnote-group @@ -32,7 +33,8 @@ .. [3] Support for the Arm Compiler (armcc) is preliminary. .. [4] Builds that execute on Java 7 to 19 can be analyzed. The analysis understands Java 19 standard language features. .. [5] ECJ is supported when the build invokes it via the Maven Compiler plugin or the Takari Lifecycle plugin. - .. [6] JSX and Flow code, YAML, JSON, HTML, and XML files may also be analyzed with JavaScript files. - .. [7] The extractor requires Python 3 to run. To analyze Python 2.7 you should install both versions of Python. - .. [8] Requires glibc 2.17. - .. [9] TypeScript analysis is performed by running the JavaScript extractor with TypeScript enabled. This is the default for LGTM. + .. [6] Kotlin support is currently in beta. + .. [7] JSX and Flow code, YAML, JSON, HTML, and XML files may also be analyzed with JavaScript files. + .. [8] The extractor requires Python 3 to run. To analyze Python 2.7 you should install both versions of Python. + .. [9] Requires glibc 2.17. + .. [10] TypeScript analysis is performed by running the JavaScript extractor with TypeScript enabled. This is the default for LGTM. diff --git a/go/extractor/trap/labels.go b/go/extractor/trap/labels.go index 1e9bd298447..f1572fb874e 100644 --- a/go/extractor/trap/labels.go +++ b/go/extractor/trap/labels.go @@ -57,7 +57,7 @@ func (l *Labeler) GlobalID(key string) Label { label, exists := l.keyLabels[key] if !exists { id := l.nextID() - fmt.Fprintf(l.tw.zip, "%s=@\"%s\"\n", id, escapeString(key)) + fmt.Fprintf(l.tw.wzip, "%s=@\"%s\"\n", id, escapeString(key)) label = Label{id} l.keyLabels[key] = label } @@ -90,7 +90,7 @@ func (l *Labeler) LocalID(nd interface{}) Label { // FreshID creates a fresh label and returns it func (l *Labeler) FreshID() Label { id := l.nextID() - fmt.Fprintf(l.tw.zip, "%s=*\n", id) + fmt.Fprintf(l.tw.wzip, "%s=*\n", id) return Label{id} } diff --git a/go/extractor/trap/trapwriter.go b/go/extractor/trap/trapwriter.go index 4c0911e24d7..0833d845830 100644 --- a/go/extractor/trap/trapwriter.go +++ b/go/extractor/trap/trapwriter.go @@ -19,7 +19,8 @@ import ( // A Writer provides methods for writing data to a TRAP file type Writer struct { zip *gzip.Writer - w *bufio.Writer + wzip *bufio.Writer + wfile *bufio.Writer file *os.File Labeler *Labeler path string @@ -54,11 +55,13 @@ func NewWriter(path string, pkg *packages.Package) (*Writer, error) { if err != nil { return nil, err } - bufioWriter := bufio.NewWriter(tmpFile) - zipWriter := gzip.NewWriter(bufioWriter) + bufioFileWriter := bufio.NewWriter(tmpFile) + zipWriter := gzip.NewWriter(bufioFileWriter) + bufioZipWriter := bufio.NewWriter(zipWriter) tw := &Writer{ zipWriter, - bufioWriter, + bufioZipWriter, + bufioFileWriter, tmpFile, nil, path, @@ -88,13 +91,19 @@ func trapFolder() (string, error) { // Close the underlying file writer func (tw *Writer) Close() error { - err := tw.zip.Close() + err := tw.wzip.Flush() + if err != nil { + // throw away file close error + tw.file.Close() + return err + } + err = tw.zip.Close() if err != nil { // return zip-close error, but ignore file-close error tw.file.Close() return err } - err = tw.w.Flush() + err = tw.wfile.Flush() if err != nil { // throw away close error because write errors are likely to be more important tw.file.Close() @@ -145,24 +154,24 @@ func capStringLength(s string) string { // Emit writes out a tuple of values for the given `table` func (tw *Writer) Emit(table string, values []interface{}) error { - fmt.Fprintf(tw.zip, "%s(", table) + fmt.Fprintf(tw.wzip, "%s(", table) for i, value := range values { if i > 0 { - fmt.Fprint(tw.zip, ", ") + fmt.Fprint(tw.wzip, ", ") } switch value := value.(type) { case Label: - fmt.Fprint(tw.zip, value.id) + fmt.Fprint(tw.wzip, value.id) case string: - fmt.Fprintf(tw.zip, "\"%s\"", escapeString(capStringLength(value))) + fmt.Fprintf(tw.wzip, "\"%s\"", escapeString(capStringLength(value))) case int: - fmt.Fprintf(tw.zip, "%d", value) + fmt.Fprintf(tw.wzip, "%d", value) case float64: - fmt.Fprintf(tw.zip, "%e", value) + fmt.Fprintf(tw.wzip, "%e", value) default: return errors.New("Cannot emit value") } } - fmt.Fprintf(tw.zip, ")\n") + fmt.Fprintf(tw.wzip, ")\n") return nil } diff --git a/java/downgrades/44d61b266bebf261cb027872646262e645efa059/old.dbscheme b/java/downgrades/44d61b266bebf261cb027872646262e645efa059/old.dbscheme new file mode 100644 index 00000000000..44d61b266be --- /dev/null +++ b/java/downgrades/44d61b266bebf261cb027872646262e645efa059/old.dbscheme @@ -0,0 +1,1246 @@ +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * javac A.java B.java C.java + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * javac A.java B.java C.java + */ + unique int id : @compilation, + int kind: int ref, + string cwd : string ref, + string name : string ref +); + +case @compilation.kind of + 1 = @javacompilation +| 2 = @kotlincompilation +; + +compilation_started( + int id : @compilation ref +) + +compilation_info( + int id : @compilation ref, + string info_key: string ref, + string info_value: string ref +) + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--javac-args` + * 2 | A.java + * 3 | B.java + * 4 | C.java + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | A.java + * 1 | B.java + * 2 | C.java + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * For each file recorded in `compilation_compiling_files`, + * there will be a corresponding row in + * `compilation_compiling_files_completed` once extraction + * of that file is complete. The `result` will indicate the + * extraction result: + * + * 0: Successfully extracted + * 1: Errors were encountered, but extraction recovered + * 2: Errors were encountered, and extraction could not recover + */ +#keyset[id, num] +compilation_compiling_files_completed( + int id : @compilation ref, + int num : int ref, + int result : int ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * The `cpu_seconds` and `elapsed_seconds` are the CPU time and elapsed + * time (respectively) that the original compilation (not the extraction) + * took for compiler invocation `id`. + */ +compilation_compiler_times( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + * The `result` will indicate the extraction result: + * + * 0: Successfully extracted + * 1: Errors were encountered, but extraction recovered + * 2: Errors were encountered, and extraction could not recover + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref, + int result : int ref +); + +diagnostics( + unique int id: @diagnostic, + string generated_by: string ref, // TODO: Sync this with the other languages? + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +/* + * External artifacts + */ + +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +snapshotDate( + unique date snapshotDate : date ref +); + +sourceLocationPrefix( + string prefix : string ref +); + +/* + * Duplicate code + */ + +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +@duplication_or_similarity = @duplication | @similarity + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/* + * SMAP + */ + +smap_header( + int outputFileId: @file ref, + string outputFilename: string ref, + string defaultStratum: string ref +); + +smap_files( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + string inputFileName: string ref, + int inputFileId: @file ref +); + +smap_lines( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + int inputStartLine: int ref, + int inputLineCount: int ref, + int outputStartLine: int ref, + int outputLineIncrement: int ref +); + +/* + * Locations and files + */ + +@location = @location_default ; + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +hasLocation( + int locatableid: @locatable ref, + int id: @location ref +); + +@sourceline = @locatable ; + +#keyset[element_id] +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +/* + * Java + */ + +cupackage( + unique int id: @file ref, + int packageid: @package ref +); + +#keyset[fileid,keyName] +jarManifestMain( + int fileid: @file ref, + string keyName: string ref, + string value: string ref +); + +#keyset[fileid,entryName,keyName] +jarManifestEntries( + int fileid: @file ref, + string entryName: string ref, + string keyName: string ref, + string value: string ref +); + +packages( + unique int id: @package, + string nodeName: string ref +); + +primitives( + unique int id: @primitive, + string nodeName: string ref +); + +modifiers( + unique int id: @modifier, + string nodeName: string ref +); + +/** + * An errortype is used when the extractor is unable to extract a type + * correctly for some reason. + */ +error_type( + unique int id: @errortype +); + +classes( + unique int id: @class, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @class ref +); + +file_class( + int id: @class ref +); + +class_object( + unique int id: @class ref, + unique int instance: @field ref +); + +type_companion_object( + unique int id: @classorinterface ref, + unique int instance: @field ref, + unique int companion_object: @class ref +); + +kt_nullable_types( + unique int id: @kt_nullable_type, + int classid: @reftype ref +) + +kt_notnull_types( + unique int id: @kt_notnull_type, + int classid: @reftype ref +) + +kt_type_alias( + unique int id: @kt_type_alias, + string name: string ref, + int kttypeid: @kt_type ref +) + +@kt_type = @kt_nullable_type | @kt_notnull_type + +isRecord( + unique int id: @class ref +); + +interfaces( + unique int id: @interface, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @interface ref +); + +fielddecls( + unique int id: @fielddecl, + int parentid: @reftype ref +); + +#keyset[fieldId] #keyset[fieldDeclId,pos] +fieldDeclaredIn( + int fieldId: @field ref, + int fieldDeclId: @fielddecl ref, + int pos: int ref +); + +fields( + unique int id: @field, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @field ref +); + +fieldsKotlinType( + unique int id: @field ref, + int kttypeid: @kt_type ref +); + +constrs( + unique int id: @constructor, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @constructor ref +); + +constrsKotlinType( + unique int id: @constructor ref, + int kttypeid: @kt_type ref +); + +methods( + unique int id: @method, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @method ref +); + +methodsKotlinType( + unique int id: @method ref, + int kttypeid: @kt_type ref +); + +#keyset[parentid,pos] +params( + unique int id: @param, + int typeid: @type ref, + int pos: int ref, + int parentid: @callable ref, + int sourceid: @param ref +); + +paramsKotlinType( + unique int id: @param ref, + int kttypeid: @kt_type ref +); + +paramName( + unique int id: @param ref, + string nodeName: string ref +); + +isVarargsParam( + int param: @param ref +); + +exceptions( + unique int id: @exception, + int typeid: @type ref, + int parentid: @callable ref +); + +isAnnotType( + int interfaceid: @interface ref +); + +isAnnotElem( + int methodid: @method ref +); + +annotValue( + int parentid: @annotation ref, + int id2: @method ref, + unique int value: @expr ref +); + +isEnumType( + int classid: @class ref +); + +isEnumConst( + int fieldid: @field ref +); + +#keyset[parentid,pos] +typeVars( + unique int id: @typevariable, + string nodeName: string ref, + int pos: int ref, + int kind: int ref, // deprecated + int parentid: @classorinterfaceorcallable ref +); + +wildcards( + unique int id: @wildcard, + string nodeName: string ref, + int kind: int ref +); + +#keyset[parentid,pos] +typeBounds( + unique int id: @typebound, + int typeid: @reftype ref, + int pos: int ref, + int parentid: @boundedtype ref +); + +#keyset[parentid,pos] +typeArgs( + int argumentid: @reftype ref, + int pos: int ref, + int parentid: @classorinterfaceorcallable ref +); + +isParameterized( + int memberid: @member ref +); + +isRaw( + int memberid: @member ref +); + +erasure( + unique int memberid: @member ref, + int erasureid: @member ref +); + +#keyset[classid] #keyset[parent] +isAnonymClass( + int classid: @class ref, + int parent: @classinstancexpr ref +); + +#keyset[typeid] #keyset[parent] +isLocalClassOrInterface( + int typeid: @classorinterface ref, + int parent: @localtypedeclstmt ref +); + +isDefConstr( + int constructorid: @constructor ref +); + +#keyset[exprId] +lambdaKind( + int exprId: @lambdaexpr ref, + int bodyKind: int ref +); + +arrays( + unique int id: @array, + string nodeName: string ref, + int elementtypeid: @type ref, + int dimension: int ref, + int componenttypeid: @type ref +); + +enclInReftype( + unique int child: @reftype ref, + int parent: @reftype ref +); + +extendsReftype( + int id1: @reftype ref, + int id2: @classorinterface ref +); + +implInterface( + int id1: @classorarray ref, + int id2: @interface ref +); + +permits( + int id1: @classorinterface ref, + int id2: @classorinterface ref +); + +hasModifier( + int id1: @modifiable ref, + int id2: @modifier ref +); + +imports( + unique int id: @import, + int holder: @classorinterfaceorpackage ref, + string name: string ref, + int kind: int ref +); + +#keyset[parent,idx] +stmts( + unique int id: @stmt, + int kind: int ref, + int parent: @stmtparent ref, + int idx: int ref, + int bodydecl: @callable ref +); + +@stmtparent = @callable | @stmt | @switchexpr | @whenexpr| @stmtexpr; + +case @stmt.kind of + 0 = @block +| 1 = @ifstmt +| 2 = @forstmt +| 3 = @enhancedforstmt +| 4 = @whilestmt +| 5 = @dostmt +| 6 = @trystmt +| 7 = @switchstmt +| 8 = @synchronizedstmt +| 9 = @returnstmt +| 10 = @throwstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @emptystmt +| 14 = @exprstmt +| 15 = @labeledstmt +| 16 = @assertstmt +| 17 = @localvariabledeclstmt +| 18 = @localtypedeclstmt +| 19 = @constructorinvocationstmt +| 20 = @superconstructorinvocationstmt +| 21 = @case +| 22 = @catchclause +| 23 = @yieldstmt +| 24 = @errorstmt +| 25 = @whenbranch +; + +#keyset[parent,idx] +exprs( + unique int id: @expr, + int kind: int ref, + int typeid: @type ref, + int parent: @exprparent ref, + int idx: int ref +); + +exprsKotlinType( + unique int id: @expr ref, + int kttypeid: @kt_type ref +); + +callableEnclosingExpr( + unique int id: @expr ref, + int callable_id: @callable ref +); + +statementEnclosingExpr( + unique int id: @expr ref, + int statement_id: @stmt ref +); + +isParenthesized( + unique int id: @expr ref, + int parentheses: int ref +); + +case @expr.kind of + 1 = @arrayaccess +| 2 = @arraycreationexpr +| 3 = @arrayinit +| 4 = @assignexpr +| 5 = @assignaddexpr +| 6 = @assignsubexpr +| 7 = @assignmulexpr +| 8 = @assigndivexpr +| 9 = @assignremexpr +| 10 = @assignandexpr +| 11 = @assignorexpr +| 12 = @assignxorexpr +| 13 = @assignlshiftexpr +| 14 = @assignrshiftexpr +| 15 = @assignurshiftexpr +| 16 = @booleanliteral +| 17 = @integerliteral +| 18 = @longliteral +| 19 = @floatingpointliteral +| 20 = @doubleliteral +| 21 = @characterliteral +| 22 = @stringliteral +| 23 = @nullliteral +| 24 = @mulexpr +| 25 = @divexpr +| 26 = @remexpr +| 27 = @addexpr +| 28 = @subexpr +| 29 = @lshiftexpr +| 30 = @rshiftexpr +| 31 = @urshiftexpr +| 32 = @andbitexpr +| 33 = @orbitexpr +| 34 = @xorbitexpr +| 35 = @andlogicalexpr +| 36 = @orlogicalexpr +| 37 = @ltexpr +| 38 = @gtexpr +| 39 = @leexpr +| 40 = @geexpr +| 41 = @eqexpr +| 42 = @neexpr +| 43 = @postincexpr +| 44 = @postdecexpr +| 45 = @preincexpr +| 46 = @predecexpr +| 47 = @minusexpr +| 48 = @plusexpr +| 49 = @bitnotexpr +| 50 = @lognotexpr +| 51 = @castexpr +| 52 = @newexpr +| 53 = @conditionalexpr +| 54 = @parexpr // deprecated +| 55 = @instanceofexpr +| 56 = @localvariabledeclexpr +| 57 = @typeliteral +| 58 = @thisaccess +| 59 = @superaccess +| 60 = @varaccess +| 61 = @methodaccess +| 62 = @unannotatedtypeaccess +| 63 = @arraytypeaccess +| 64 = @packageaccess +| 65 = @wildcardtypeaccess +| 66 = @declannotation +| 67 = @uniontypeaccess +| 68 = @lambdaexpr +| 69 = @memberref +| 70 = @annotatedtypeaccess +| 71 = @typeannotation +| 72 = @intersectiontypeaccess +| 73 = @switchexpr +| 74 = @errorexpr +| 75 = @whenexpr +| 76 = @getclassexpr +| 77 = @safecastexpr +| 78 = @implicitcastexpr +| 79 = @implicitnotnullexpr +| 80 = @implicitcoerciontounitexpr +| 81 = @notinstanceofexpr +| 82 = @stmtexpr +| 83 = @stringtemplateexpr +| 84 = @notnullexpr +| 85 = @unsafecoerceexpr +| 86 = @valueeqexpr +| 87 = @valueneexpr +| 88 = @propertyref +; + +/** Holds if this `when` expression was written as an `if` expression. */ +when_if(unique int id: @whenexpr ref); + +/** Holds if this `when` branch was written as an `else` branch. */ +when_branch_else(unique int id: @whenbranch ref); + +@classinstancexpr = @newexpr | @lambdaexpr | @memberref | @propertyref + +@annotation = @declannotation | @typeannotation +@typeaccess = @unannotatedtypeaccess | @annotatedtypeaccess + +@assignment = @assignexpr + | @assignop; + +@unaryassignment = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr; + +@assignop = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + | @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignurshiftexpr; + +@literal = @booleanliteral + | @integerliteral + | @longliteral + | @floatingpointliteral + | @doubleliteral + | @characterliteral + | @stringliteral + | @nullliteral; + +@binaryexpr = @mulexpr + | @divexpr + | @remexpr + | @addexpr + | @subexpr + | @lshiftexpr + | @rshiftexpr + | @urshiftexpr + | @andbitexpr + | @orbitexpr + | @xorbitexpr + | @andlogicalexpr + | @orlogicalexpr + | @ltexpr + | @gtexpr + | @leexpr + | @geexpr + | @eqexpr + | @neexpr + | @valueeqexpr + | @valueneexpr; + +@unaryexpr = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr + | @minusexpr + | @plusexpr + | @bitnotexpr + | @lognotexpr + | @notnullexpr; + +@caller = @classinstancexpr + | @methodaccess + | @constructorinvocationstmt + | @superconstructorinvocationstmt; + +callableBinding( + unique int callerid: @caller ref, + int callee: @callable ref +); + +memberRefBinding( + unique int id: @expr ref, + int callable: @callable ref +); + +propertyRefGetBinding( + unique int id: @expr ref, + int getter: @callable ref +); + +propertyRefFieldBinding( + unique int id: @expr ref, + int field: @field ref +); + +propertyRefSetBinding( + unique int id: @expr ref, + int setter: @callable ref +); + +@exprparent = @stmt | @expr | @whenbranch | @callable | @field | @fielddecl | @class | @interface | @param | @localvar | @typevariable; + +variableBinding( + unique int expr: @varaccess ref, + int variable: @variable ref +); + +@variable = @localscopevariable | @field; + +@localscopevariable = @localvar | @param; + +localvars( + unique int id: @localvar, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @localvariabledeclexpr ref +); + +localvarsKotlinType( + unique int id: @localvar ref, + int kttypeid: @kt_type ref +); + +@namedexprorstmt = @breakstmt + | @continuestmt + | @labeledstmt + | @literal; + +namestrings( + string name: string ref, + string value: string ref, + unique int parent: @namedexprorstmt ref +); + +/* + * Modules + */ + +#keyset[name] +modules( + unique int id: @module, + string name: string ref +); + +isOpen( + int id: @module ref +); + +#keyset[fileId] +cumodule( + int fileId: @file ref, + int moduleId: @module ref +); + +@directive = @requires + | @exports + | @opens + | @uses + | @provides + +#keyset[directive] +directives( + int id: @module ref, + int directive: @directive ref +); + +requires( + unique int id: @requires, + int target: @module ref +); + +isTransitive( + int id: @requires ref +); + +isStatic( + int id: @requires ref +); + +exports( + unique int id: @exports, + int target: @package ref +); + +exportsTo( + int id: @exports ref, + int target: @module ref +); + +opens( + unique int id: @opens, + int target: @package ref +); + +opensTo( + int id: @opens ref, + int target: @module ref +); + +uses( + unique int id: @uses, + string serviceInterface: string ref +); + +provides( + unique int id: @provides, + string serviceInterface: string ref +); + +providesWith( + int id: @provides ref, + string serviceImpl: string ref +); + +/* + * Javadoc + */ + +javadoc( + unique int id: @javadoc +); + +isNormalComment( + int commentid : @javadoc ref +); + +isEolComment( + int commentid : @javadoc ref +); + +hasJavadoc( + int documentableid: @member ref, + int javadocid: @javadoc ref +); + +#keyset[parentid,idx] +javadocTag( + unique int id: @javadocTag, + string name: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +#keyset[parentid,idx] +javadocText( + unique int id: @javadocText, + string text: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +@javadocParent = @javadoc | @javadocTag; +@javadocElement = @javadocTag | @javadocText; + +@classorinterface = @interface | @class; +@classorinterfaceorpackage = @classorinterface | @package; +@classorinterfaceorcallable = @classorinterface | @callable; +@boundedtype = @typevariable | @wildcard; +@reftype = @classorinterface | @array | @boundedtype | @errortype; +@classorarray = @class | @array; +@type = @primitive | @reftype; +@callable = @method | @constructor; + +/** A program element that has a name. */ +@element = @package | @modifier | @annotation | @errortype | + @locatableElement; + +@locatableElement = @file | @primitive | @class | @interface | @method | @constructor | @param | @exception | @field | + @boundedtype | @array | @localvar | @expr | @stmt | @import | @fielddecl | @kt_type | @kt_type_alias | + @kt_property; + +@modifiable = @member_modifiable| @param | @localvar | @typevariable; + +@member_modifiable = @class | @interface | @method | @constructor | @field | @kt_property; + +@member = @method | @constructor | @field | @reftype ; + +/** A program element that has a location. */ +@locatable = @typebound | @javadoc | @javadocTag | @javadocText | @xmllocatable | @ktcomment | + @locatableElement; + +@top = @element | @locatable | @folder; + +/* + * XML Files + */ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/* + * configuration files with key value pairs + */ + +configs( + unique int id: @config +); + +configNames( + unique int id: @configName, + int config: @config ref, + string name: string ref +); + +configValues( + unique int id: @configValue, + int config: @config ref, + string value: string ref +); + +configLocations( + int locatable: @configLocatable ref, + int location: @location_default ref +); + +@configLocatable = @config | @configName | @configValue; + +ktComments( + unique int id: @ktcomment, + int kind: int ref, + string text : string ref +) + +ktCommentSections( + unique int id: @ktcommentsection, + int comment: @ktcomment ref, + string content : string ref +) + +ktCommentSectionNames( + unique int id: @ktcommentsection ref, + string name : string ref +) + +ktCommentSectionSubjectNames( + unique int id: @ktcommentsection ref, + string subjectname : string ref +) + +#keyset[id, owner] +ktCommentOwners( + int id: @ktcomment ref, + int owner: @top ref +) + +ktExtensionFunctions( + unique int id: @method ref, + int typeid: @type ref, + int kttypeid: @kt_type ref +) + +ktProperties( + unique int id: @kt_property, + string nodeName: string ref +) + +ktPropertyGetters( + unique int id: @kt_property ref, + int getter: @method ref +) + +ktPropertySetters( + unique int id: @kt_property ref, + int setter: @method ref +) + +ktPropertyBackingFields( + unique int id: @kt_property ref, + int backingField: @field ref +) + +ktSyntheticBody( + unique int id: @callable ref, + int kind: int ref + // 1: ENUM_VALUES + // 2: ENUM_VALUEOF +) + +ktLocalFunction( + unique int id: @method ref +) + +ktInitializerAssignment( + unique int id: @assignexpr ref +) + +ktPropertyDelegates( + unique int id: @kt_property ref, + unique int variableId: @variable ref +) + +/** + * If `id` is a compiler generated element, then the kind indicates the + * reason that the compiler generated it. + * See `Element.compilerGeneratedReason()` for an explanation of what + * each `kind` means. + */ +compiler_generated( + unique int id: @element ref, + int kind: int ref +) + +ktFunctionOriginalNames( + unique int id: @method ref, + string name: string ref +) + +ktDataClasses( + unique int id: @class ref +) diff --git a/java/downgrades/44d61b266bebf261cb027872646262e645efa059/semmlecode.dbscheme b/java/downgrades/44d61b266bebf261cb027872646262e645efa059/semmlecode.dbscheme new file mode 100644 index 00000000000..709f1d1fd04 --- /dev/null +++ b/java/downgrades/44d61b266bebf261cb027872646262e645efa059/semmlecode.dbscheme @@ -0,0 +1,1240 @@ +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * javac A.java B.java C.java + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * javac A.java B.java C.java + */ + unique int id : @compilation, + int kind: int ref, + string cwd : string ref, + string name : string ref +); + +case @compilation.kind of + 1 = @javacompilation +| 2 = @kotlincompilation +; + +compilation_started( + int id : @compilation ref +) + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--javac-args` + * 2 | A.java + * 3 | B.java + * 4 | C.java + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | A.java + * 1 | B.java + * 2 | C.java + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * For each file recorded in `compilation_compiling_files`, + * there will be a corresponding row in + * `compilation_compiling_files_completed` once extraction + * of that file is complete. The `result` will indicate the + * extraction result: + * + * 0: Successfully extracted + * 1: Errors were encountered, but extraction recovered + * 2: Errors were encountered, and extraction could not recover + */ +#keyset[id, num] +compilation_compiling_files_completed( + int id : @compilation ref, + int num : int ref, + int result : int ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * The `cpu_seconds` and `elapsed_seconds` are the CPU time and elapsed + * time (respectively) that the original compilation (not the extraction) + * took for compiler invocation `id`. + */ +compilation_compiler_times( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + * The `result` will indicate the extraction result: + * + * 0: Successfully extracted + * 1: Errors were encountered, but extraction recovered + * 2: Errors were encountered, and extraction could not recover + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref, + int result : int ref +); + +diagnostics( + unique int id: @diagnostic, + string generated_by: string ref, // TODO: Sync this with the other languages? + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +/* + * External artifacts + */ + +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +snapshotDate( + unique date snapshotDate : date ref +); + +sourceLocationPrefix( + string prefix : string ref +); + +/* + * Duplicate code + */ + +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +@duplication_or_similarity = @duplication | @similarity + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/* + * SMAP + */ + +smap_header( + int outputFileId: @file ref, + string outputFilename: string ref, + string defaultStratum: string ref +); + +smap_files( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + string inputFileName: string ref, + int inputFileId: @file ref +); + +smap_lines( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + int inputStartLine: int ref, + int inputLineCount: int ref, + int outputStartLine: int ref, + int outputLineIncrement: int ref +); + +/* + * Locations and files + */ + +@location = @location_default ; + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +hasLocation( + int locatableid: @locatable ref, + int id: @location ref +); + +@sourceline = @locatable ; + +#keyset[element_id] +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +/* + * Java + */ + +cupackage( + unique int id: @file ref, + int packageid: @package ref +); + +#keyset[fileid,keyName] +jarManifestMain( + int fileid: @file ref, + string keyName: string ref, + string value: string ref +); + +#keyset[fileid,entryName,keyName] +jarManifestEntries( + int fileid: @file ref, + string entryName: string ref, + string keyName: string ref, + string value: string ref +); + +packages( + unique int id: @package, + string nodeName: string ref +); + +primitives( + unique int id: @primitive, + string nodeName: string ref +); + +modifiers( + unique int id: @modifier, + string nodeName: string ref +); + +/** + * An errortype is used when the extractor is unable to extract a type + * correctly for some reason. + */ +error_type( + unique int id: @errortype +); + +classes( + unique int id: @class, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @class ref +); + +file_class( + int id: @class ref +); + +class_object( + unique int id: @class ref, + unique int instance: @field ref +); + +type_companion_object( + unique int id: @classorinterface ref, + unique int instance: @field ref, + unique int companion_object: @class ref +); + +kt_nullable_types( + unique int id: @kt_nullable_type, + int classid: @reftype ref +) + +kt_notnull_types( + unique int id: @kt_notnull_type, + int classid: @reftype ref +) + +kt_type_alias( + unique int id: @kt_type_alias, + string name: string ref, + int kttypeid: @kt_type ref +) + +@kt_type = @kt_nullable_type | @kt_notnull_type + +isRecord( + unique int id: @class ref +); + +interfaces( + unique int id: @interface, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @interface ref +); + +fielddecls( + unique int id: @fielddecl, + int parentid: @reftype ref +); + +#keyset[fieldId] #keyset[fieldDeclId,pos] +fieldDeclaredIn( + int fieldId: @field ref, + int fieldDeclId: @fielddecl ref, + int pos: int ref +); + +fields( + unique int id: @field, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @field ref +); + +fieldsKotlinType( + unique int id: @field ref, + int kttypeid: @kt_type ref +); + +constrs( + unique int id: @constructor, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @constructor ref +); + +constrsKotlinType( + unique int id: @constructor ref, + int kttypeid: @kt_type ref +); + +methods( + unique int id: @method, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @method ref +); + +methodsKotlinType( + unique int id: @method ref, + int kttypeid: @kt_type ref +); + +#keyset[parentid,pos] +params( + unique int id: @param, + int typeid: @type ref, + int pos: int ref, + int parentid: @callable ref, + int sourceid: @param ref +); + +paramsKotlinType( + unique int id: @param ref, + int kttypeid: @kt_type ref +); + +paramName( + unique int id: @param ref, + string nodeName: string ref +); + +isVarargsParam( + int param: @param ref +); + +exceptions( + unique int id: @exception, + int typeid: @type ref, + int parentid: @callable ref +); + +isAnnotType( + int interfaceid: @interface ref +); + +isAnnotElem( + int methodid: @method ref +); + +annotValue( + int parentid: @annotation ref, + int id2: @method ref, + unique int value: @expr ref +); + +isEnumType( + int classid: @class ref +); + +isEnumConst( + int fieldid: @field ref +); + +#keyset[parentid,pos] +typeVars( + unique int id: @typevariable, + string nodeName: string ref, + int pos: int ref, + int kind: int ref, // deprecated + int parentid: @classorinterfaceorcallable ref +); + +wildcards( + unique int id: @wildcard, + string nodeName: string ref, + int kind: int ref +); + +#keyset[parentid,pos] +typeBounds( + unique int id: @typebound, + int typeid: @reftype ref, + int pos: int ref, + int parentid: @boundedtype ref +); + +#keyset[parentid,pos] +typeArgs( + int argumentid: @reftype ref, + int pos: int ref, + int parentid: @classorinterfaceorcallable ref +); + +isParameterized( + int memberid: @member ref +); + +isRaw( + int memberid: @member ref +); + +erasure( + unique int memberid: @member ref, + int erasureid: @member ref +); + +#keyset[classid] #keyset[parent] +isAnonymClass( + int classid: @class ref, + int parent: @classinstancexpr ref +); + +#keyset[typeid] #keyset[parent] +isLocalClassOrInterface( + int typeid: @classorinterface ref, + int parent: @localtypedeclstmt ref +); + +isDefConstr( + int constructorid: @constructor ref +); + +#keyset[exprId] +lambdaKind( + int exprId: @lambdaexpr ref, + int bodyKind: int ref +); + +arrays( + unique int id: @array, + string nodeName: string ref, + int elementtypeid: @type ref, + int dimension: int ref, + int componenttypeid: @type ref +); + +enclInReftype( + unique int child: @reftype ref, + int parent: @reftype ref +); + +extendsReftype( + int id1: @reftype ref, + int id2: @classorinterface ref +); + +implInterface( + int id1: @classorarray ref, + int id2: @interface ref +); + +permits( + int id1: @classorinterface ref, + int id2: @classorinterface ref +); + +hasModifier( + int id1: @modifiable ref, + int id2: @modifier ref +); + +imports( + unique int id: @import, + int holder: @classorinterfaceorpackage ref, + string name: string ref, + int kind: int ref +); + +#keyset[parent,idx] +stmts( + unique int id: @stmt, + int kind: int ref, + int parent: @stmtparent ref, + int idx: int ref, + int bodydecl: @callable ref +); + +@stmtparent = @callable | @stmt | @switchexpr | @whenexpr| @stmtexpr; + +case @stmt.kind of + 0 = @block +| 1 = @ifstmt +| 2 = @forstmt +| 3 = @enhancedforstmt +| 4 = @whilestmt +| 5 = @dostmt +| 6 = @trystmt +| 7 = @switchstmt +| 8 = @synchronizedstmt +| 9 = @returnstmt +| 10 = @throwstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @emptystmt +| 14 = @exprstmt +| 15 = @labeledstmt +| 16 = @assertstmt +| 17 = @localvariabledeclstmt +| 18 = @localtypedeclstmt +| 19 = @constructorinvocationstmt +| 20 = @superconstructorinvocationstmt +| 21 = @case +| 22 = @catchclause +| 23 = @yieldstmt +| 24 = @errorstmt +| 25 = @whenbranch +; + +#keyset[parent,idx] +exprs( + unique int id: @expr, + int kind: int ref, + int typeid: @type ref, + int parent: @exprparent ref, + int idx: int ref +); + +exprsKotlinType( + unique int id: @expr ref, + int kttypeid: @kt_type ref +); + +callableEnclosingExpr( + unique int id: @expr ref, + int callable_id: @callable ref +); + +statementEnclosingExpr( + unique int id: @expr ref, + int statement_id: @stmt ref +); + +isParenthesized( + unique int id: @expr ref, + int parentheses: int ref +); + +case @expr.kind of + 1 = @arrayaccess +| 2 = @arraycreationexpr +| 3 = @arrayinit +| 4 = @assignexpr +| 5 = @assignaddexpr +| 6 = @assignsubexpr +| 7 = @assignmulexpr +| 8 = @assigndivexpr +| 9 = @assignremexpr +| 10 = @assignandexpr +| 11 = @assignorexpr +| 12 = @assignxorexpr +| 13 = @assignlshiftexpr +| 14 = @assignrshiftexpr +| 15 = @assignurshiftexpr +| 16 = @booleanliteral +| 17 = @integerliteral +| 18 = @longliteral +| 19 = @floatingpointliteral +| 20 = @doubleliteral +| 21 = @characterliteral +| 22 = @stringliteral +| 23 = @nullliteral +| 24 = @mulexpr +| 25 = @divexpr +| 26 = @remexpr +| 27 = @addexpr +| 28 = @subexpr +| 29 = @lshiftexpr +| 30 = @rshiftexpr +| 31 = @urshiftexpr +| 32 = @andbitexpr +| 33 = @orbitexpr +| 34 = @xorbitexpr +| 35 = @andlogicalexpr +| 36 = @orlogicalexpr +| 37 = @ltexpr +| 38 = @gtexpr +| 39 = @leexpr +| 40 = @geexpr +| 41 = @eqexpr +| 42 = @neexpr +| 43 = @postincexpr +| 44 = @postdecexpr +| 45 = @preincexpr +| 46 = @predecexpr +| 47 = @minusexpr +| 48 = @plusexpr +| 49 = @bitnotexpr +| 50 = @lognotexpr +| 51 = @castexpr +| 52 = @newexpr +| 53 = @conditionalexpr +| 54 = @parexpr // deprecated +| 55 = @instanceofexpr +| 56 = @localvariabledeclexpr +| 57 = @typeliteral +| 58 = @thisaccess +| 59 = @superaccess +| 60 = @varaccess +| 61 = @methodaccess +| 62 = @unannotatedtypeaccess +| 63 = @arraytypeaccess +| 64 = @packageaccess +| 65 = @wildcardtypeaccess +| 66 = @declannotation +| 67 = @uniontypeaccess +| 68 = @lambdaexpr +| 69 = @memberref +| 70 = @annotatedtypeaccess +| 71 = @typeannotation +| 72 = @intersectiontypeaccess +| 73 = @switchexpr +| 74 = @errorexpr +| 75 = @whenexpr +| 76 = @getclassexpr +| 77 = @safecastexpr +| 78 = @implicitcastexpr +| 79 = @implicitnotnullexpr +| 80 = @implicitcoerciontounitexpr +| 81 = @notinstanceofexpr +| 82 = @stmtexpr +| 83 = @stringtemplateexpr +| 84 = @notnullexpr +| 85 = @unsafecoerceexpr +| 86 = @valueeqexpr +| 87 = @valueneexpr +| 88 = @propertyref +; + +/** Holds if this `when` expression was written as an `if` expression. */ +when_if(unique int id: @whenexpr ref); + +/** Holds if this `when` branch was written as an `else` branch. */ +when_branch_else(unique int id: @whenbranch ref); + +@classinstancexpr = @newexpr | @lambdaexpr | @memberref | @propertyref + +@annotation = @declannotation | @typeannotation +@typeaccess = @unannotatedtypeaccess | @annotatedtypeaccess + +@assignment = @assignexpr + | @assignop; + +@unaryassignment = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr; + +@assignop = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + | @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignurshiftexpr; + +@literal = @booleanliteral + | @integerliteral + | @longliteral + | @floatingpointliteral + | @doubleliteral + | @characterliteral + | @stringliteral + | @nullliteral; + +@binaryexpr = @mulexpr + | @divexpr + | @remexpr + | @addexpr + | @subexpr + | @lshiftexpr + | @rshiftexpr + | @urshiftexpr + | @andbitexpr + | @orbitexpr + | @xorbitexpr + | @andlogicalexpr + | @orlogicalexpr + | @ltexpr + | @gtexpr + | @leexpr + | @geexpr + | @eqexpr + | @neexpr + | @valueeqexpr + | @valueneexpr; + +@unaryexpr = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr + | @minusexpr + | @plusexpr + | @bitnotexpr + | @lognotexpr + | @notnullexpr; + +@caller = @classinstancexpr + | @methodaccess + | @constructorinvocationstmt + | @superconstructorinvocationstmt; + +callableBinding( + unique int callerid: @caller ref, + int callee: @callable ref +); + +memberRefBinding( + unique int id: @expr ref, + int callable: @callable ref +); + +propertyRefGetBinding( + unique int id: @expr ref, + int getter: @callable ref +); + +propertyRefFieldBinding( + unique int id: @expr ref, + int field: @field ref +); + +propertyRefSetBinding( + unique int id: @expr ref, + int setter: @callable ref +); + +@exprparent = @stmt | @expr | @whenbranch | @callable | @field | @fielddecl | @class | @interface | @param | @localvar | @typevariable; + +variableBinding( + unique int expr: @varaccess ref, + int variable: @variable ref +); + +@variable = @localscopevariable | @field; + +@localscopevariable = @localvar | @param; + +localvars( + unique int id: @localvar, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @localvariabledeclexpr ref +); + +localvarsKotlinType( + unique int id: @localvar ref, + int kttypeid: @kt_type ref +); + +@namedexprorstmt = @breakstmt + | @continuestmt + | @labeledstmt + | @literal; + +namestrings( + string name: string ref, + string value: string ref, + unique int parent: @namedexprorstmt ref +); + +/* + * Modules + */ + +#keyset[name] +modules( + unique int id: @module, + string name: string ref +); + +isOpen( + int id: @module ref +); + +#keyset[fileId] +cumodule( + int fileId: @file ref, + int moduleId: @module ref +); + +@directive = @requires + | @exports + | @opens + | @uses + | @provides + +#keyset[directive] +directives( + int id: @module ref, + int directive: @directive ref +); + +requires( + unique int id: @requires, + int target: @module ref +); + +isTransitive( + int id: @requires ref +); + +isStatic( + int id: @requires ref +); + +exports( + unique int id: @exports, + int target: @package ref +); + +exportsTo( + int id: @exports ref, + int target: @module ref +); + +opens( + unique int id: @opens, + int target: @package ref +); + +opensTo( + int id: @opens ref, + int target: @module ref +); + +uses( + unique int id: @uses, + string serviceInterface: string ref +); + +provides( + unique int id: @provides, + string serviceInterface: string ref +); + +providesWith( + int id: @provides ref, + string serviceImpl: string ref +); + +/* + * Javadoc + */ + +javadoc( + unique int id: @javadoc +); + +isNormalComment( + int commentid : @javadoc ref +); + +isEolComment( + int commentid : @javadoc ref +); + +hasJavadoc( + int documentableid: @member ref, + int javadocid: @javadoc ref +); + +#keyset[parentid,idx] +javadocTag( + unique int id: @javadocTag, + string name: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +#keyset[parentid,idx] +javadocText( + unique int id: @javadocText, + string text: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +@javadocParent = @javadoc | @javadocTag; +@javadocElement = @javadocTag | @javadocText; + +@classorinterface = @interface | @class; +@classorinterfaceorpackage = @classorinterface | @package; +@classorinterfaceorcallable = @classorinterface | @callable; +@boundedtype = @typevariable | @wildcard; +@reftype = @classorinterface | @array | @boundedtype | @errortype; +@classorarray = @class | @array; +@type = @primitive | @reftype; +@callable = @method | @constructor; + +/** A program element that has a name. */ +@element = @package | @modifier | @annotation | @errortype | + @locatableElement; + +@locatableElement = @file | @primitive | @class | @interface | @method | @constructor | @param | @exception | @field | + @boundedtype | @array | @localvar | @expr | @stmt | @import | @fielddecl | @kt_type | @kt_type_alias | + @kt_property; + +@modifiable = @member_modifiable| @param | @localvar | @typevariable; + +@member_modifiable = @class | @interface | @method | @constructor | @field | @kt_property; + +@member = @method | @constructor | @field | @reftype ; + +/** A program element that has a location. */ +@locatable = @typebound | @javadoc | @javadocTag | @javadocText | @xmllocatable | @ktcomment | + @locatableElement; + +@top = @element | @locatable | @folder; + +/* + * XML Files + */ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/* + * configuration files with key value pairs + */ + +configs( + unique int id: @config +); + +configNames( + unique int id: @configName, + int config: @config ref, + string name: string ref +); + +configValues( + unique int id: @configValue, + int config: @config ref, + string value: string ref +); + +configLocations( + int locatable: @configLocatable ref, + int location: @location_default ref +); + +@configLocatable = @config | @configName | @configValue; + +ktComments( + unique int id: @ktcomment, + int kind: int ref, + string text : string ref +) + +ktCommentSections( + unique int id: @ktcommentsection, + int comment: @ktcomment ref, + string content : string ref +) + +ktCommentSectionNames( + unique int id: @ktcommentsection ref, + string name : string ref +) + +ktCommentSectionSubjectNames( + unique int id: @ktcommentsection ref, + string subjectname : string ref +) + +#keyset[id, owner] +ktCommentOwners( + int id: @ktcomment ref, + int owner: @top ref +) + +ktExtensionFunctions( + unique int id: @method ref, + int typeid: @type ref, + int kttypeid: @kt_type ref +) + +ktProperties( + unique int id: @kt_property, + string nodeName: string ref +) + +ktPropertyGetters( + unique int id: @kt_property ref, + int getter: @method ref +) + +ktPropertySetters( + unique int id: @kt_property ref, + int setter: @method ref +) + +ktPropertyBackingFields( + unique int id: @kt_property ref, + int backingField: @field ref +) + +ktSyntheticBody( + unique int id: @callable ref, + int kind: int ref + // 1: ENUM_VALUES + // 2: ENUM_VALUEOF +) + +ktLocalFunction( + unique int id: @method ref +) + +ktInitializerAssignment( + unique int id: @assignexpr ref +) + +ktPropertyDelegates( + unique int id: @kt_property ref, + unique int variableId: @variable ref +) + +/** + * If `id` is a compiler generated element, then the kind indicates the + * reason that the compiler generated it. + * See `Element.compilerGeneratedReason()` for an explanation of what + * each `kind` means. + */ +compiler_generated( + unique int id: @element ref, + int kind: int ref +) + +ktFunctionOriginalNames( + unique int id: @method ref, + string name: string ref +) + +ktDataClasses( + unique int id: @class ref +) diff --git a/java/downgrades/44d61b266bebf261cb027872646262e645efa059/upgrade.properties b/java/downgrades/44d61b266bebf261cb027872646262e645efa059/upgrade.properties new file mode 100644 index 00000000000..8d5b41b6e56 --- /dev/null +++ b/java/downgrades/44d61b266bebf261cb027872646262e645efa059/upgrade.properties @@ -0,0 +1,3 @@ +description: Remove compilation_info +compatibility: full +compilation_info.rel: delete diff --git a/java/kotlin-extractor/build.py b/java/kotlin-extractor/build.py index 9525522869b..05cb1204d86 100755 --- a/java/kotlin-extractor/build.py +++ b/java/kotlin-extractor/build.py @@ -103,7 +103,7 @@ def compile_to_dir(srcs, classpath, java_classpath, output): '-classpath', os.path.pathsep.join([output, classpath, java_classpath])] + [s for s in srcs if s.endswith(".java")]) -def compile_to_jar(build_dir, srcs, classpath, java_classpath, output): +def compile_to_jar(build_dir, tmp_src_dir, srcs, classpath, java_classpath, output): class_dir = build_dir + '/classes' if os.path.exists(class_dir): @@ -114,7 +114,8 @@ def compile_to_jar(build_dir, srcs, classpath, java_classpath, output): run_process(['jar', 'cf', output, '-C', class_dir, '.', - '-C', 'src/main/resources', 'META-INF']) + '-C', tmp_src_dir + '/main/resources', 'META-INF', + '-C', tmp_src_dir + '/main/resources', 'com/github/codeql/extractor.name']) shutil.rmtree(class_dir) @@ -185,6 +186,11 @@ def compile(jars, java_jars, dependency_folder, transform_to_embeddable, output, include_version_folder = tmp_src_dir + '/main/kotlin/utils/versions/to_include' os.makedirs(include_version_folder) + resource_dir = tmp_src_dir + '/main/resources/com/github/codeql' + os.makedirs(resource_dir) + with open(resource_dir + '/extractor.name', 'w') as f: + f.write(output) + parsed_current_version = kotlin_plugin_versions.version_string_to_tuple( current_version) @@ -211,7 +217,7 @@ def compile(jars, java_jars, dependency_folder, transform_to_embeddable, output, transform_to_embeddable(srcs) - compile_to_jar(build_dir, srcs, classpath, java_classpath, output) + compile_to_jar(build_dir, tmp_src_dir, srcs, classpath, java_classpath, output) shutil.rmtree(tmp_src_dir) diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt b/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt index c11d8569ae4..71e1862a22e 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt @@ -131,6 +131,9 @@ class KotlinExtractorExtension( // The interceptor has already defined #compilation = * val compilation: Label = StringLabel("compilation") tw.writeCompilation_started(compilation) + tw.writeCompilation_info(compilation, "Kotlin Compiler Version", KotlinCompilerVersion.getVersion() ?: "") + val extractor_name = this::class.java.getResource("extractor.name")?.readText() ?: "" + tw.writeCompilation_info(compilation, "Kotlin Extractor Name", extractor_name) if (compilationStartTime != null) { tw.writeCompilation_compiler_times(compilation, -1.0, (System.currentTimeMillis()-compilationStartTime)/1000.0) } diff --git a/java/kotlin-extractor/src/main/kotlin/utils/Logger.kt b/java/kotlin-extractor/src/main/kotlin/utils/Logger.kt index 43c6ee4cc10..36ebda9dd1c 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/Logger.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/Logger.kt @@ -138,6 +138,10 @@ open class LoggerBase(val logCounter: LogCounter) { fullMsgBuilder.append(suffix) val fullMsg = fullMsgBuilder.toString() + emitDiagnostic(tw, severity, diagnosticLocStr, msg, fullMsg, locationString, mkLocationId) + } + + private fun emitDiagnostic(tw: TrapWriter, severity: Severity, diagnosticLocStr: String, msg: String, fullMsg: String, locationString: String? = null, mkLocationId: () -> Label = { tw.unknownLocation }) { val locStr = if (locationString == null) "" else "At " + locationString + ": " val kind = if (severity <= Severity.WarnHigh) "WARN" else "ERROR" val logMessage = LogMessage(kind, "Diagnostic($diagnosticLocStr): $locStr$fullMsg") @@ -190,9 +194,10 @@ open class LoggerBase(val logCounter: LogCounter) { // We don't know if this location relates to an error // or a warning, so we just declare hitting the limit // to be an error regardless. - val logMessage = LogMessage("ERROR", "Total of $count diagnostics from $caller.") - tw.writeComment(logMessage.toText()) - logStream.write(logMessage.toJsonLine()) + val message = "Total of $count diagnostics (reached limit of ${logCounter.diagnosticLimit}) from $caller." + if (verbosity >= 1) { + emitDiagnostic(tw, Severity.Error, "Limit", message, message) + } } } } diff --git a/java/ql/integration-tests/all-platforms/kotlin/enabling/class.expected b/java/ql/integration-tests/all-platforms/kotlin/enabling/class.expected index 8c8a04f83ce..f39ac4aa815 100644 --- a/java/ql/integration-tests/all-platforms/kotlin/enabling/class.expected +++ b/java/ql/integration-tests/all-platforms/kotlin/enabling/class.expected @@ -1 +1,2 @@ +| KotlinDefault.kt:1:1:1:22 | KotlinDefault | | KotlinEnabled.kt:1:1:1:22 | KotlinEnabled | diff --git a/java/ql/lib/change-notes/2022-10-31-shared-redos-pack.md b/java/ql/lib/change-notes/2022-10-31-shared-redos-pack.md new file mode 100644 index 00000000000..405ddd1108c --- /dev/null +++ b/java/ql/lib/change-notes/2022-10-31-shared-redos-pack.md @@ -0,0 +1,4 @@ +--- + category: minorAnalysis +--- + * The ReDoS libraries in `semmle.code.java.security.regexp` has been moved to a shared pack inside the `shared/` folder, and the previous location has been deprecated. \ No newline at end of file diff --git a/java/ql/lib/change-notes/2022-11-10-getInfo.md b/java/ql/lib/change-notes/2022-11-10-getInfo.md new file mode 100644 index 00000000000..7a113ca3459 --- /dev/null +++ b/java/ql/lib/change-notes/2022-11-10-getInfo.md @@ -0,0 +1,4 @@ +--- +category: feature +--- +* The new `string Compilation.getInfo(string)` provides access to some information about compilations. diff --git a/java/ql/lib/change-notes/2022-11-10-kotlin-default.md b/java/ql/lib/change-notes/2022-11-10-kotlin-default.md new file mode 100644 index 00000000000..d411c58173c --- /dev/null +++ b/java/ql/lib/change-notes/2022-11-10-kotlin-default.md @@ -0,0 +1,4 @@ +--- +category: feature +--- +* Kotlin support is now in beta. This means that Java analyses will also include Kotlin code by default. Kotlin support can be disabled by setting `CODEQL_EXTRACTOR_JAVA_AGENT_DISABLE_KOTLIN` to `true` in the environment. diff --git a/java/ql/lib/config/semmlecode.dbscheme b/java/ql/lib/config/semmlecode.dbscheme index 709f1d1fd04..44d61b266be 100644 --- a/java/ql/lib/config/semmlecode.dbscheme +++ b/java/ql/lib/config/semmlecode.dbscheme @@ -31,6 +31,12 @@ compilation_started( int id : @compilation ref ) +compilation_info( + int id : @compilation ref, + string info_key: string ref, + string info_value: string ref +) + /** * The arguments that were passed to the extractor for a compiler * invocation. If `id` is for the compiler invocation diff --git a/java/ql/lib/config/semmlecode.dbscheme.stats b/java/ql/lib/config/semmlecode.dbscheme.stats index 21f4ae2aa28..7ebc6e0b93e 100644 --- a/java/ql/lib/config/semmlecode.dbscheme.stats +++ b/java/ql/lib/config/semmlecode.dbscheme.stats @@ -6,11 +6,11 @@ @kotlincompilation - 6806 + 7683 @diagnostic - 631661 + 61632 @externalDataElement @@ -26,27 +26,27 @@ @file - 7997871 + 9158642 @folder - 1275575 + 1415670 @location_default - 430051334 + 602747369 @package - 611241 - - - @modifier - 13613 + 580098 @primitive - 12252 + 17287 + + + @modifier + 26891 @errortype @@ -54,87 +54,87 @@ @class - 12548830 + 12618104 @kt_nullable_type - 1361 + 1920 @kt_notnull_type - 191419 + 172391 @kt_type_alias - 2060 + 1821 @interface - 21501109 + 23200100 @fielddecl - 398872 + 199475 @field - 27509955 + 19350704 @constructor - 6869320 + 8006128 @method - 93308954 + 109719302 @param - 101015498 + 120852585 @exception - 1228169 + 1232644 @typevariable - 5105024 + 6288883 @wildcard - 3629331 + 3338447 @typebound - 4383514 + 4229725 @array - 1116298 + 1375332 @import - 368532 + 368550 @block - 845392 + 756817 @ifstmt - 188296 + 188285 @forstmt - 52508 + 52504 @enhancedforstmt - 18506 + 18520 @whilestmt - 21684 + 13266 @dostmt @@ -142,7 +142,7 @@ @trystmt - 58627 + 58624 @switchstmt @@ -150,19 +150,19 @@ @synchronizedstmt - 18703 + 18206 @returnstmt - 674863 + 532846 @throwstmt - 35919 + 35917 @breakstmt - 35331 + 35329 @continuestmt @@ -170,23 +170,23 @@ @emptystmt - 1562 + 1561 @exprstmt - 942161 + 942102 @assertstmt - 10816 + 10815 @localvariabledeclstmt - 318709 + 318689 @localtypedeclstmt - 4057 + 3841 @constructorinvocationstmt @@ -194,19 +194,19 @@ @superconstructorinvocationstmt - 223641 + 201354 @case - 107943 + 107945 @catchclause - 55205 + 55201 @labeledstmt - 2560 + 2342 @yieldstmt @@ -218,27 +218,27 @@ @whenbranch - 234276 + 225101 @arrayaccess - 409706 + 409681 @arraycreationexpr - 69251 + 69247 @arrayinit - 405408 + 405405 @assignexpr - 465437 + 465407 @assignaddexpr - 17017 + 17016 @assignsubexpr @@ -258,63 +258,63 @@ @assignorexpr - 14529 + 14528 @booleanliteral - 589884 + 589912 @integerliteral - 1151527 + 1151458 @longliteral - 185903 + 185904 @floatingpointliteral - 2825166 + 2824996 @doubleliteral - 486650 + 486619 @characterliteral - 40018 + 40016 @stringliteral - 1262892 + 1262818 @nullliteral - 355828 + 434113 @mulexpr - 204593 + 204580 @divexpr - 36267 + 36264 @remexpr - 3896 + 3904 @addexpr - 176997 + 176986 @subexpr - 84390 + 84385 @lshiftexpr - 8737 + 8736 @rshiftexpr @@ -326,11 +326,11 @@ @andbitexpr - 29091 + 110212 @orbitexpr - 8626 + 12590 @xorbitexpr @@ -338,47 +338,47 @@ @andlogicalexpr - 41339 + 36742 @orlogicalexpr - 31152 + 31150 @ltexpr - 66254 + 66249 @gtexpr - 17204 + 17203 @leexpr - 10530 + 10529 @geexpr - 13412 + 13411 @eqexpr - 104281 + 128429 @neexpr - 60978 + 60975 @postincexpr - 29634 + 29632 @postdecexpr - 11684 + 11683 @preincexpr - 23716 + 23714 @predecexpr @@ -386,75 +386,75 @@ @minusexpr - 744431 + 744386 @plusexpr - 53010 + 53007 @bitnotexpr - 8187 + 8186 @lognotexpr - 40113 + 40111 @castexpr - 93193 + 93187 @newexpr - 252099 + 252083 @conditionalexpr - 16048 + 16047 @instanceofexpr - 31040 + 29542 @localvariabledeclexpr - 385297 + 385272 @typeliteral - 148289 + 144350 @thisaccess - 949960 + 756915 @superaccess - 22195 + 99884 @varaccess - 2434432 + 2434277 @methodaccess - 1578817 + 1512385 @unannotatedtypeaccess - 2861937 + 2608638 @arraytypeaccess - 120735 + 120727 @wildcardtypeaccess - 63960 + 64091 @declannotation - 6745248 + 6740832 @assignremexpr @@ -462,7 +462,7 @@ @assignxorexpr - 1101 + 1102 @assignlshiftexpr @@ -490,19 +490,19 @@ @lambdaexpr - 183936 + 167982 @memberref - 23858 + 23859 @annotatedtypeaccess - 1280 + 1281 @typeannotation - 1280 + 1281 @intersectiontypeaccess @@ -518,43 +518,43 @@ @whenexpr - 172064 + 152111 @getclassexpr - 1361 + 1920 @safecastexpr - 6932 + 6402 @implicitcastexpr - 32918 + 30316 @implicitnotnullexpr - 241262 + 216630 @implicitcoerciontounitexpr - 91329 + 81396 @notinstanceofexpr - 19576 + 17306 @stmtexpr - 57687 + 55704 @stringtemplateexpr - 55814 + 59546 @notnullexpr - 20290 + 18820 @unsafecoerceexpr @@ -562,23 +562,23 @@ @valueeqexpr - 102712 + 93060 @valueneexpr - 108184 + 95639 @propertyref - 9642 + 8878 @localvar - 385297 + 385272 @module - 7964 + 7965 @requires @@ -586,7 +586,7 @@ @exports - 35011 + 35013 @opens @@ -594,7 +594,7 @@ @uses - 10785 + 10786 @provides @@ -602,15 +602,15 @@ @javadoc - 985153 + 985091 @javadocTag - 335830 + 335808 @javadocText - 2503007 + 2502848 @xmldtd @@ -618,23 +618,23 @@ @xmlelement - 106507143 + 150282022 @xmlattribute - 129551904 + 182798274 @xmlnamespace - 8168 + 11525 @xmlcomment - 107198704 + 151257816 @xmlcharacters - 101302741 + 142938589 @config @@ -650,15 +650,15 @@ @ktcomment - 133411 + 188243 @ktcommentsection - 59896 + 52611 @kt_property - 30236718 + 21895839 @@ -880,30 +880,146 @@ compilation_started - 6806 + 7683 id - 6806 + 7683 - compilation_args - 157915 + compilation_info + 15366 id - 6806 + 7683 + + + info_key + 3841 + + + info_value + 3841 + + + + + id + info_key + + + 12 + + + 2 + 3 + 7683 + + + + + + + id + info_value + + + 12 + + + 2 + 3 + 7683 + + + + + + + info_key + id + + + 12 + + + 4 + 5 + 3841 + + + + + + + info_key + info_value + + + 12 + + + 1 + 2 + 3841 + + + + + + + info_value + id + + + 12 + + + 4 + 5 + 3841 + + + + + + + info_value + info_key + + + 12 + + + 1 + 2 + 3841 + + + + + + + + + compilation_args + 169035 + + + id + 7683 num - 38117 + 48021 arg - 89848 + 90280 @@ -917,22 +1033,17 @@ 20 21 - 2722 + 3841 23 24 - 1361 + 1920 25 26 - 1361 - - - 28 - 29 - 1361 + 1920 @@ -948,22 +1059,17 @@ 20 21 - 2722 + 3841 23 24 - 1361 + 1920 25 26 - 1361 - - - 27 - 28 - 1361 + 1920 @@ -979,22 +1085,17 @@ 1 2 - 4084 + 3841 2 3 - 2722 + 5762 - 3 - 4 - 4084 - - - 5 - 6 - 27226 + 4 + 5 + 38417 @@ -1010,27 +1111,22 @@ 1 2 - 8168 + 9604 2 3 - 5445 + 17287 3 4 - 10890 + 11525 4 5 - 6806 - - - 5 - 6 - 6806 + 9604 @@ -1046,22 +1142,17 @@ 1 2 - 69428 + 61467 2 3 - 2722 + 3841 4 5 - 6806 - - - 5 - 6 - 10890 + 24971 @@ -1077,17 +1168,17 @@ 1 2 - 72151 + 67229 2 3 - 14974 + 19208 - 4 - 5 - 2722 + 3 + 4 + 3841 @@ -1097,19 +1188,19 @@ compilation_compiling_files - 61119 + 59495 id - 2337 + 2275 num - 18035 + 17556 file - 51099 + 49742 @@ -1123,22 +1214,22 @@ 1 2 - 333 + 325 2 3 - 667 + 650 35 36 - 667 + 650 54 55 - 667 + 650 @@ -1154,22 +1245,22 @@ 1 2 - 333 + 325 2 3 - 667 + 650 35 36 - 667 + 650 54 55 - 667 + 650 @@ -1185,17 +1276,17 @@ 2 3 - 6345 + 6177 4 5 - 11021 + 10728 6 8 - 667 + 650 @@ -1211,17 +1302,17 @@ 2 3 - 6345 + 6177 3 4 - 10019 + 9753 4 8 - 1669 + 1625 @@ -1237,12 +1328,12 @@ 1 2 - 41080 + 39989 2 3 - 10019 + 9753 @@ -1258,7 +1349,7 @@ 1 2 - 51099 + 49742 @@ -1268,19 +1359,19 @@ compilation_compiling_files_completed - 61119 + 59495 id - 2337 + 2275 num - 18035 + 17556 result - 667 + 325 @@ -1294,22 +1385,22 @@ 1 2 - 333 + 325 2 3 - 667 + 650 35 36 - 667 + 650 54 55 - 667 + 650 @@ -1325,12 +1416,7 @@ 1 2 - 1669 - - - 2 - 3 - 667 + 2275 @@ -1346,17 +1432,17 @@ 2 3 - 6345 + 6177 4 5 - 11021 + 10728 6 8 - 667 + 650 @@ -1372,12 +1458,7 @@ 1 2 - 17701 - - - 2 - 3 - 333 + 17556 @@ -1390,15 +1471,10 @@ 12 - - 2 - 3 - 333 - 7 8 - 333 + 325 @@ -1411,15 +1487,10 @@ 12 - - 1 - 2 - 333 - 54 55 - 333 + 325 @@ -1429,7 +1500,7 @@ compilation_time - 196462 + 196471 id @@ -1437,7 +1508,7 @@ num - 5143 + 5144 kind @@ -1445,7 +1516,7 @@ seconds - 89768 + 89772 @@ -1658,7 +1729,7 @@ 4 5 - 5143 + 5144 @@ -1793,7 +1864,7 @@ 1 2 - 89602 + 89606 52 @@ -1814,7 +1885,7 @@ 1 2 - 89602 + 89606 31 @@ -1835,7 +1906,7 @@ 1 2 - 89602 + 89606 3 @@ -1850,23 +1921,23 @@ diagnostic_for - 631661 + 61632 diagnostic - 631661 + 61632 compilation - 6806 + 574 file_number - 8168 + 14797 file_number_diagnostic_number - 59898 + 2154 @@ -1880,7 +1951,7 @@ 1 2 - 631661 + 61632 @@ -1896,7 +1967,7 @@ 1 2 - 631661 + 61632 @@ -1912,7 +1983,7 @@ 1 2 - 631661 + 61632 @@ -1926,24 +1997,24 @@ 12 - 73 - 74 - 1361 + 14 + 15 + 143 - 84 - 85 - 1361 + 28 + 29 + 143 - 100 - 101 - 2722 + 123 + 124 + 143 - 107 - 108 - 1361 + 264 + 265 + 143 @@ -1957,24 +2028,24 @@ 12 - 3 - 4 - 2722 + 7 + 8 + 143 - 4 - 5 - 1361 + 14 + 15 + 143 - 5 - 6 - 1361 + 45 + 46 + 143 - 6 - 7 - 1361 + 103 + 104 + 143 @@ -1988,29 +2059,19 @@ 12 - 29 - 30 - 1361 + 2 + 3 + 287 - 34 - 35 - 1361 + 14 + 15 + 143 - 40 - 41 - 1361 - - - 42 - 43 - 1361 - - - 44 - 45 - 1361 + 15 + 16 + 143 @@ -2019,47 +2080,6 @@ file_number diagnostic - - - 12 - - - 6 - 7 - 1361 - - - 35 - 36 - 1361 - - - 49 - 50 - 1361 - - - 101 - 102 - 1361 - - - 129 - 130 - 1361 - - - 144 - 145 - 1361 - - - - - - - file_number - compilation 12 @@ -2067,22 +2087,73 @@ 1 2 - 1361 + 143 2 3 - 1361 + 6464 3 4 - 1361 + 718 + + + 4 + 5 + 3160 5 6 - 4084 + 1292 + + + 6 + 8 + 1149 + + + 8 + 11 + 1292 + + + 12 + 21 + 574 + + + + + + + file_number + compilation + + + 12 + + + 1 + 2 + 8332 + + + 2 + 3 + 4453 + + + 3 + 4 + 1005 + + + 4 + 5 + 1005 @@ -2096,34 +2167,34 @@ 12 - 6 - 7 - 1361 + 1 + 2 + 143 - 15 + 2 + 3 + 10056 + + + 3 + 4 + 2011 + + + 4 + 5 + 1005 + + + 5 + 8 + 1149 + + + 10 16 - 1361 - - - 34 - 35 - 1361 - - - 40 - 41 - 1361 - - - 42 - 43 - 1361 - - - 44 - 45 - 1361 + 430 @@ -2138,58 +2209,53 @@ 1 - 3 - 5445 + 2 + 143 - 4 - 5 - 6806 + 2 + 3 + 574 + + + 3 + 4 + 430 5 6 - 1361 + 143 7 8 - 6806 + 143 - 8 - 9 - 4084 - - - 9 - 10 - 9529 - - - 10 - 14 - 5445 - - - 15 - 16 - 2722 - - - 16 - 17 - 8168 + 11 + 12 + 143 18 - 20 - 4084 + 19 + 143 - 20 - 22 - 5445 + 33 + 34 + 143 + + + 168 + 169 + 143 + + + 169 + 170 + 143 @@ -2204,23 +2270,18 @@ 1 - 3 - 5445 + 2 + 143 - 3 - 4 - 8168 + 2 + 3 + 1723 4 5 - 6806 - - - 5 - 6 - 39478 + 287 @@ -2235,28 +2296,53 @@ 1 + 2 + 143 + + + 2 3 - 5445 + 574 3 4 - 8168 - - - 4 - 5 - 25865 + 430 5 6 - 12252 + 143 - 6 - 7 - 8168 + 7 + 8 + 143 + + + 11 + 12 + 143 + + + 18 + 19 + 143 + + + 32 + 33 + 143 + + + 102 + 103 + 143 + + + 103 + 104 + 143 @@ -2266,19 +2352,19 @@ compilation_compiler_times - 6806 + 7683 id - 6806 + 7683 cpu_seconds - 1361 + 1920 elapsed_seconds - 6806 + 7683 @@ -2292,7 +2378,7 @@ 1 2 - 6806 + 7683 @@ -2308,7 +2394,7 @@ 1 2 - 6806 + 7683 @@ -2322,9 +2408,9 @@ 12 - 5 - 6 - 1361 + 4 + 5 + 1920 @@ -2338,9 +2424,9 @@ 12 - 5 - 6 - 1361 + 4 + 5 + 1920 @@ -2356,7 +2442,7 @@ 1 2 - 6806 + 7683 @@ -2372,7 +2458,7 @@ 1 2 - 6806 + 7683 @@ -2598,35 +2684,35 @@ diagnostics - 631661 + 61632 id - 631661 + 61632 generated_by - 1361 + 143 severity - 1361 + 143 error_tag - 1361 + 143 error_message - 96655 + 1580 full_error_message - 520031 + 40944 location - 1361 + 143 @@ -2640,7 +2726,7 @@ 1 2 - 631661 + 61632 @@ -2656,7 +2742,7 @@ 1 2 - 631661 + 61632 @@ -2672,7 +2758,7 @@ 1 2 - 631661 + 61632 @@ -2688,7 +2774,7 @@ 1 2 - 631661 + 61632 @@ -2704,7 +2790,7 @@ 1 2 - 631661 + 61632 @@ -2720,7 +2806,7 @@ 1 2 - 631661 + 61632 @@ -2734,9 +2820,9 @@ 12 - 464 - 465 - 1361 + 429 + 430 + 143 @@ -2752,7 +2838,7 @@ 1 2 - 1361 + 143 @@ -2768,7 +2854,7 @@ 1 2 - 1361 + 143 @@ -2782,9 +2868,9 @@ 12 - 71 - 72 - 1361 + 11 + 12 + 143 @@ -2798,9 +2884,9 @@ 12 - 382 - 383 - 1361 + 285 + 286 + 143 @@ -2816,7 +2902,7 @@ 1 2 - 1361 + 143 @@ -2830,9 +2916,9 @@ 12 - 464 - 465 - 1361 + 429 + 430 + 143 @@ -2848,7 +2934,7 @@ 1 2 - 1361 + 143 @@ -2864,7 +2950,7 @@ 1 2 - 1361 + 143 @@ -2878,9 +2964,9 @@ 12 - 71 - 72 - 1361 + 11 + 12 + 143 @@ -2894,9 +2980,9 @@ 12 - 382 - 383 - 1361 + 285 + 286 + 143 @@ -2912,7 +2998,7 @@ 1 2 - 1361 + 143 @@ -2926,9 +3012,9 @@ 12 - 464 - 465 - 1361 + 429 + 430 + 143 @@ -2944,7 +3030,7 @@ 1 2 - 1361 + 143 @@ -2960,7 +3046,7 @@ 1 2 - 1361 + 143 @@ -2974,9 +3060,9 @@ 12 - 71 - 72 - 1361 + 11 + 12 + 143 @@ -2990,9 +3076,9 @@ 12 - 382 - 383 - 1361 + 285 + 286 + 143 @@ -3008,7 +3094,7 @@ 1 2 - 1361 + 143 @@ -3024,62 +3110,47 @@ 1 2 - 19058 + 143 2 3 - 5445 + 143 3 4 - 12252 - - - 4 - 5 - 5445 - - - 5 - 6 - 5445 + 143 6 7 - 8168 - - - 7 - 8 - 2722 - - - 8 - 9 - 9529 + 143 9 10 - 6806 + 143 - 10 - 11 - 8168 + 12 + 13 + 287 - 14 - 17 - 6806 + 24 + 25 + 143 - 17 - 21 - 6806 + 28 + 29 + 143 + + + 166 + 167 + 287 @@ -3095,7 +3166,7 @@ 1 2 - 96655 + 1580 @@ -3111,7 +3182,7 @@ 1 2 - 96655 + 1580 @@ -3127,7 +3198,7 @@ 1 2 - 96655 + 1580 @@ -3143,57 +3214,52 @@ 1 2 - 21781 + 143 2 3 - 5445 + 143 3 4 - 12252 - - - 4 - 5 - 5445 - - - 5 - 6 - 4084 + 143 6 7 - 8168 - - - 7 - 8 - 12252 - - - 8 - 9 - 6806 + 143 9 - 11 - 8168 - - - 11 - 12 - 6806 + 10 + 143 12 - 14 - 5445 + 13 + 287 + + + 22 + 23 + 143 + + + 24 + 25 + 143 + + + 28 + 29 + 143 + + + 166 + 167 + 143 @@ -3209,7 +3275,7 @@ 1 2 - 96655 + 1580 @@ -3225,17 +3291,12 @@ 1 2 - 441074 + 38358 2 - 3 - 51730 - - - 3 - 5 - 27226 + 25 + 2585 @@ -3251,7 +3312,7 @@ 1 2 - 520031 + 40944 @@ -3267,7 +3328,7 @@ 1 2 - 520031 + 40944 @@ -3283,7 +3344,7 @@ 1 2 - 520031 + 40944 @@ -3299,7 +3360,7 @@ 1 2 - 520031 + 40944 @@ -3315,7 +3376,7 @@ 1 2 - 520031 + 40944 @@ -3329,9 +3390,9 @@ 12 - 464 - 465 - 1361 + 429 + 430 + 143 @@ -3347,7 +3408,7 @@ 1 2 - 1361 + 143 @@ -3363,7 +3424,7 @@ 1 2 - 1361 + 143 @@ -3379,7 +3440,7 @@ 1 2 - 1361 + 143 @@ -3393,9 +3454,9 @@ 12 - 71 - 72 - 1361 + 11 + 12 + 143 @@ -3409,9 +3470,9 @@ 12 - 382 - 383 - 1361 + 285 + 286 + 143 @@ -3576,11 +3637,11 @@ sourceLocationPrefix - 1361 + 1920 prefix - 1361 + 1920 @@ -4867,31 +4928,31 @@ locations_default - 430051334 + 602747369 id - 430051334 + 602747369 file - 7997871 + 9158642 beginLine - 2794830 + 3943517 beginColumn - 175612 + 247790 endLine - 2796191 + 3945438 endColumn - 619409 + 873989 @@ -4905,7 +4966,7 @@ 1 2 - 430051334 + 602747369 @@ -4921,7 +4982,7 @@ 1 2 - 430051334 + 602747369 @@ -4937,7 +4998,7 @@ 1 2 - 430051334 + 602747369 @@ -4953,7 +5014,7 @@ 1 2 - 430051334 + 602747369 @@ -4969,7 +5030,7 @@ 1 2 - 430051334 + 602747369 @@ -4985,17 +5046,17 @@ 1 2 - 7159286 + 7986919 2 - 20 - 603073 + 11 + 714558 - 20 + 11 3605 - 235511 + 457163 @@ -5011,17 +5072,17 @@ 1 2 - 7159286 + 7986919 2 - 15 - 600350 + 9 + 712637 - 15 + 9 1830 - 238234 + 459084 @@ -5037,17 +5098,17 @@ 1 2 - 7159286 + 7986919 2 - 7 - 627577 + 5 + 776025 - 7 + 5 105 - 211007 + 395696 @@ -5063,17 +5124,17 @@ 1 2 - 7159286 + 7986919 2 - 18 - 600350 + 10 + 693429 - 18 + 10 1834 - 238234 + 478293 @@ -5089,17 +5150,17 @@ 1 2 - 7159286 + 7986919 2 - 15 - 603073 + 9 + 695349 - 15 + 9 205 - 235511 + 476372 @@ -5115,67 +5176,67 @@ 1 14 - 221898 + 313099 14 125 - 215091 + 301574 125 142 - 215091 + 307336 142 152 - 223259 + 316941 152 159 - 249125 + 359200 159 - 165 - 257293 + 164 + 272761 - 165 - 170 - 225982 + 164 + 169 + 343833 - 170 - 174 - 216453 + 169 + 173 + 299653 - 174 - 179 - 228705 + 173 + 178 + 332308 - 179 - 185 - 213730 + 178 + 184 + 347674 - 185 - 194 - 221898 + 184 + 193 + 316941 - 194 - 215 - 215091 + 193 + 211 + 297732 - 215 - 5876 - 91209 + 211 + 4769 + 134459 @@ -5191,67 +5252,72 @@ 1 7 - 228705 + 322703 7 65 - 212369 + 299653 65 73 - 216453 + 307336 73 78 - 206923 + 295811 78 81 - 190587 + 265078 81 84 - 249125 + 357279 84 86 - 215091 + 299653 86 - 88 - 257293 + 87 + 188243 - 88 - 90 - 221898 + 87 + 89 + 357279 - 90 - 93 - 247763 + 89 + 91 + 259315 - 93 - 97 - 217814 + 91 + 94 + 324624 - 97 - 106 - 213730 + 94 + 99 + 328466 - 106 - 5876 - 117075 + 99 + 141 + 295811 + + + 141 + 4769 + 42258 @@ -5267,62 +5333,62 @@ 1 5 - 221898 + 313099 5 17 - 196032 + 280444 17 19 - 167444 + 251632 19 20 - 213730 + 309257 20 21 - 268183 + 403379 21 22 - 283158 + 420667 22 23 - 329444 + 476372 23 24 - 303578 + 457163 24 25 - 235511 + 339991 25 26 - 170167 + 213215 26 - 28 - 212369 + 29 + 361120 - 28 - 45 - 193310 + 29 + 40 + 117172 @@ -5338,32 +5404,32 @@ 1 2 - 902568 + 1273527 2 3 - 894400 + 1265844 3 4 - 481914 + 674220 4 5 - 211007 + 299653 5 11 - 220537 + 307336 11 97 - 84403 + 122934 @@ -5379,72 +5445,72 @@ 1 13 - 219175 + 309257 13 60 - 209646 + 307336 60 64 - 223259 + 311178 64 66 - 209646 + 311178 66 68 - 258654 + 353437 68 69 - 130688 + 194006 69 70 - 155192 + 217056 70 - 71 - 134772 + 72 + 359200 - 71 - 73 - 235511 + 72 + 74 + 322703 - 73 - 75 - 212369 + 74 + 76 + 245869 - 75 - 78 - 234150 + 76 + 79 + 330387 - 78 - 82 - 251847 + 79 + 83 + 295811 - 82 - 89 - 213730 + 83 + 91 + 316941 - 89 - 104 - 106184 + 91 + 103 + 69150 @@ -5460,67 +5526,67 @@ 1 11 - 14974 + 21129 15 - 34 - 14974 + 24 + 21129 - 36 - 58 - 13613 + 28 + 57 + 19208 - 63 - 88 - 13613 + 57 + 79 + 19208 - 89 - 132 - 13613 + 87 + 119 + 19208 - 141 - 196 - 14974 + 130 + 177 + 19208 - 210 - 285 - 13613 + 195 + 269 + 19208 - 316 - 468 - 13613 + 270 + 436 + 19208 - 496 - 853 - 13613 + 443 + 835 + 19208 - 899 - 1420 - 13613 + 844 + 1367 + 19208 - 1472 - 2256 - 13613 + 1419 + 2155 + 19208 - 2300 - 2526 - 13613 + 2252 + 2517 + 19208 - 2589 - 226687 - 8168 + 2521 + 226452 + 13445 @@ -5536,72 +5602,72 @@ 1 9 - 9529 + 17287 9 11 - 13613 + 21129 - 12 - 16 - 8168 + 11 + 15 + 15366 - 16 + 15 19 - 13613 + 21129 - 19 - 31 - 13613 + 23 + 68 + 19208 - 35 - 73 - 13613 + 69 + 78 + 19208 - 73 - 83 - 13613 + 79 + 100 + 13445 - 85 + 100 104 - 14974 + 21129 104 - 110 - 10890 + 109 + 19208 - 110 - 114 - 14974 + 109 + 112 + 13445 + + + 112 + 115 + 19208 115 - 119 - 13613 + 117 + 19208 - 119 - 121 - 12252 + 117 + 123 + 19208 - 121 - 126 - 13613 - - - 126 - 5876 - 9529 + 145 + 4769 + 9604 @@ -5617,67 +5683,67 @@ 1 10 - 14974 + 21129 10 - 29 - 13613 + 22 + 21129 - 29 - 43 - 13613 + 23 + 39 + 19208 - 45 + 41 58 - 13613 + 19208 58 - 85 - 13613 + 84 + 19208 - 86 + 84 106 - 13613 + 19208 108 - 167 - 13613 + 166 + 19208 - 171 - 227 - 13613 + 167 + 225 + 19208 - 231 - 379 - 13613 + 230 + 376 + 19208 - 383 - 650 - 13613 + 381 + 647 + 19208 - 651 - 891 - 13613 + 657 + 941 + 19208 - 940 - 1086 - 13613 + 941 + 1090 + 19208 - 1093 + 1102 2051 - 10890 + 13445 @@ -5693,67 +5759,67 @@ 1 10 - 14974 + 21129 10 - 30 - 13613 + 22 + 21129 - 30 - 43 - 13613 + 23 + 39 + 19208 - 46 + 41 59 - 13613 + 19208 - 60 - 87 - 13613 + 59 + 86 + 19208 - 87 + 86 109 - 13613 + 19208 - 115 - 173 - 13613 + 114 + 168 + 19208 - 174 - 226 - 13613 + 170 + 224 + 19208 - 230 - 380 - 13613 + 229 + 379 + 19208 - 383 - 650 - 13613 + 382 + 647 + 19208 - 653 - 892 - 13613 + 658 + 941 + 19208 - 940 - 1084 - 13613 + 941 + 1089 + 19208 - 1092 + 1102 2051 - 10890 + 13445 @@ -5769,72 +5835,67 @@ 1 8 - 14974 + 21129 8 - 17 - 13613 + 16 + 21129 - 18 - 25 - 10890 + 16 + 23 + 21129 - 25 - 30 - 13613 + 24 + 31 + 21129 - 30 - 36 - 13613 + 32 + 37 + 21129 - 36 - 44 - 13613 + 37 + 50 + 19208 - 45 - 56 - 12252 + 50 + 60 + 19208 - 57 - 64 - 13613 + 60 + 68 + 19208 - 64 - 73 - 13613 + 68 + 80 + 19208 - 73 - 86 - 13613 + 81 + 101 + 19208 - 86 - 106 - 13613 + 101 + 121 + 19208 - 107 - 129 - 13613 + 126 + 158 + 19208 - 129 - 197 - 13613 - - - 392 + 159 393 - 1361 + 7683 @@ -5850,67 +5911,67 @@ 1 14 - 220537 + 309257 14 124 - 215091 + 305416 124 143 - 217814 + 309257 143 152 - 235511 + 334228 152 159 - 223259 + 322703 159 - 165 - 257293 + 164 + 299653 - 165 - 170 - 239595 + 164 + 169 + 341912 - 170 - 174 - 221898 + 169 + 173 + 309257 - 174 - 179 - 221898 + 173 + 178 + 338070 - 179 - 185 - 211007 + 178 + 184 + 305416 - 185 - 194 - 216453 + 184 + 193 + 315020 - 194 - 217 - 212369 + 193 + 212 + 301574 - 217 - 5876 - 103461 + 212 + 4769 + 153668 @@ -5926,67 +5987,67 @@ 1 7 - 231427 + 324624 7 66 - 224621 + 318862 66 74 - 227343 + 320782 74 80 - 250486 + 355358 80 83 - 240957 + 339991 83 85 - 185142 + 268919 85 87 - 246402 + 343833 87 89 - 246402 + 355358 89 91 - 187864 + 266999 91 94 - 247763 + 345754 94 99 - 225982 + 324624 99 - 127 - 212369 + 130 + 301574 - 127 - 5876 - 69428 + 131 + 4769 + 78755 @@ -6002,32 +6063,32 @@ 1 2 - 709258 + 1000766 2 3 - 986971 + 1392620 3 4 - 642552 + 908564 4 6 - 236873 + 336149 6 - 18 - 212369 + 19 + 299653 - 18 + 19 22 - 8168 + 7683 @@ -6043,62 +6104,62 @@ 1 5 - 219175 + 309257 5 17 - 198755 + 284286 17 19 - 163360 + 232423 19 20 - 191948 + 280444 20 21 - 280436 + 420667 21 22 - 272267 + 407221 22 23 - 329444 + 482134 23 24 - 306301 + 437955 24 25 - 224621 + 334228 25 26 - 183780 + 259315 26 - 28 - 223259 + 29 + 363041 - 28 - 44 - 202839 + 29 + 39 + 134459 @@ -6114,72 +6175,72 @@ 1 13 - 221898 + 313099 13 - 61 - 250486 + 60 + 305416 - 61 + 60 64 - 172890 + 305416 64 66 - 217814 + 303495 66 68 - 250486 + 357279 68 69 - 137495 + 197848 69 70 - 137495 + 201689 70 71 - 157915 + 218977 71 73 - 231427 + 326545 73 75 - 191948 + 263157 75 77 - 183780 + 257394 77 80 - 211007 + 299653 80 85 - 227343 + 318862 85 119 - 204200 + 276603 @@ -6195,57 +6256,57 @@ 1 2 - 145663 + 205531 2 3 - 63982 + 90280 3 5 - 50369 + 71071 5 13 - 50369 + 71071 13 53 - 47646 + 67229 53 - 146 - 47646 + 138 + 67229 - 146 - 351 - 47646 + 142 + 346 + 67229 357 - 997 - 47646 + 967 + 67229 - 1053 - 2396 - 47646 + 1050 + 2386 + 67229 - 2407 - 4957 - 47646 + 2392 + 4902 + 67229 - 5022 - 5934 - 23142 + 4949 + 5933 + 32654 @@ -6261,57 +6322,57 @@ 1 2 - 151108 + 213215 2 3 - 61260 + 86438 3 5 - 53092 + 74913 5 13 - 50369 + 71071 13 42 - 47646 + 67229 42 77 - 47646 + 67229 77 - 103 - 51730 + 102 + 69150 - 103 - 116 - 47646 + 102 + 114 + 67229 - 116 - 144 - 49008 + 114 + 139 + 67229 - 144 - 181 - 47646 + 139 + 169 + 69150 - 181 - 5876 - 12252 + 173 + 4769 + 21129 @@ -6327,57 +6388,57 @@ 1 2 - 153831 + 217056 2 3 - 62621 + 88359 3 5 - 49008 + 69150 5 13 - 49008 + 69150 13 - 52 - 49008 + 50 + 67229 - 52 - 115 - 47646 + 50 + 113 + 67229 - 123 - 271 - 47646 + 114 + 266 + 67229 - 272 - 658 - 47646 + 269 + 636 + 67229 - 669 - 1200 - 47646 + 648 + 1197 + 67229 - 1219 + 1198 1635 - 47646 + 69150 1639 1722 - 17697 + 24971 @@ -6393,47 +6454,47 @@ 1 2 - 194671 + 274682 2 3 - 74873 + 105647 3 6 - 54453 + 76834 6 14 - 54453 + 76834 14 - 26 - 47646 + 25 + 74913 - 26 + 25 36 - 49008 + 71071 36 47 - 49008 + 67229 47 - 55 - 47646 + 54 + 67229 - 55 - 68 - 47646 + 54 + 65 + 59546 @@ -6449,57 +6510,57 @@ 1 2 - 153831 + 217056 2 3 - 61260 + 86438 3 5 - 49008 + 69150 5 13 - 49008 + 69150 13 - 53 - 50369 + 51 + 67229 - 53 - 115 - 47646 + 51 + 112 + 67229 - 123 - 271 - 47646 + 112 + 262 + 67229 - 280 - 656 - 47646 + 262 + 630 + 67229 - 669 - 1200 - 47646 + 637 + 1186 + 67229 - 1217 - 1638 - 47646 + 1197 + 1625 + 67229 - 1640 + 1632 1722 - 17697 + 28812 @@ -6509,15 +6570,15 @@ hasLocation - 316413492 + 340930835 locatableid - 316270551 + 340658074 id - 11518296 + 12195515 @@ -6531,12 +6592,12 @@ 1 2 - 316127611 + 340385312 2 3 - 142940 + 272761 @@ -6552,62 +6613,57 @@ 1 2 - 2084211 + 2091812 2 3 - 996500 + 1333074 3 4 - 716064 + 772184 4 6 - 984248 + 1085283 6 - 7 - 771879 + 8 + 1062233 - 7 - 9 - 1014198 + 8 + 11 + 1100650 - 9 - 12 - 848114 + 11 + 15 + 1048787 - 12 - 16 - 928433 + 15 + 21 + 964269 - 16 - 23 - 868534 + 21 + 32 + 916248 - 23 - 39 - 894400 + 32 + 64 + 920090 - 39 - 89 - 873980 - - - 89 - 9992 - 537729 + 64 + 9549 + 900881 @@ -6617,23 +6673,23 @@ numlines - 214506316 + 303305105 element_id - 214506316 + 303305105 num_lines - 431544 + 618515 num_code - 432906 + 612753 num_comment - 1342281 + 1893964 @@ -6647,7 +6703,7 @@ 1 2 - 214506316 + 303305105 @@ -6663,7 +6719,7 @@ 1 2 - 214506316 + 303305105 @@ -6679,7 +6735,7 @@ 1 2 - 214506316 + 303305105 @@ -6695,37 +6751,37 @@ 1 2 - 231427 + 320782 2 3 - 53092 + 78755 3 4 - 44924 + 61467 4 7 - 34033 + 49942 7 14 - 32672 + 48021 15 - 839 - 32672 + 194 + 48021 - 3519 - 149603 - 2722 + 320 + 149659 + 11525 @@ -6741,12 +6797,17 @@ 1 2 - 422015 + 509026 2 3 - 9529 + 69150 + + + 3 + 6 + 40337 @@ -6762,27 +6823,27 @@ 1 2 - 273629 + 380329 2 3 - 69428 + 97963 3 4 - 36756 + 51863 4 6 - 34033 + 53783 6 987 - 17697 + 34575 @@ -6798,37 +6859,37 @@ 1 2 - 231427 + 316941 2 3 - 53092 + 78755 3 4 - 44924 + 61467 4 7 - 34033 + 51863 7 - 14 - 32672 + 15 + 46100 - 15 - 468 - 32672 + 16 + 214 + 46100 - 495 + 325 78746 - 4084 + 11525 @@ -6844,12 +6905,17 @@ 1 2 - 431544 + 516710 - 7 + 2 + 3 + 51863 + + + 3 8 - 1361 + 44179 @@ -6865,27 +6931,27 @@ 1 2 - 273629 + 370725 2 3 - 69428 + 101805 3 4 - 36756 + 53783 4 6 - 34033 + 46100 6 987 - 19058 + 40337 @@ -6901,77 +6967,77 @@ 1 7 - 108907 + 153668 7 49 - 100739 + 142143 49 71 - 104823 + 147905 71 78 - 107545 + 151747 78 83 - 103461 + 145985 83 87 - 115713 + 163272 87 89 - 99377 + 140222 89 91 - 95293 + 134459 91 92 - 57176 + 80675 92 93 - 68066 + 96042 93 94 - 74873 + 105647 94 95 - 69428 + 97963 95 97 - 108907 + 151747 97 119 - 100739 + 144064 - 119 - 75115 - 27226 + 120 + 75134 + 38417 @@ -6987,22 +7053,22 @@ 1 2 - 1101323 + 1550130 2 3 - 114352 + 165193 3 - 6 - 102100 + 7 + 145985 - 6 + 7 120 - 24504 + 32654 @@ -7018,22 +7084,22 @@ 1 2 - 1101323 + 1550130 2 3 - 114352 + 165193 3 - 6 - 100739 + 7 + 145985 - 6 - 121 - 25865 + 7 + 122 + 32654 @@ -7043,15 +7109,15 @@ files - 7997871 + 9158642 id - 7997871 + 9158642 name - 7997871 + 9158642 @@ -7065,7 +7131,7 @@ 1 2 - 7997871 + 9158642 @@ -7081,7 +7147,7 @@ 1 2 - 7997871 + 9158642 @@ -7091,15 +7157,15 @@ folders - 1275575 + 1415670 id - 1275575 + 1415670 name - 1275575 + 1415670 @@ -7113,7 +7179,7 @@ 1 2 - 1275575 + 1415670 @@ -7129,7 +7195,7 @@ 1 2 - 1275575 + 1415670 @@ -7139,15 +7205,15 @@ containerparent - 9270724 + 10570471 parent - 1316415 + 1463692 child - 9270724 + 10570471 @@ -7161,37 +7227,32 @@ 1 2 - 710619 + 843255 2 3 - 140218 + 149826 3 - 4 - 78957 + 5 + 128697 - 4 - 7 - 112991 + 5 + 11 + 124855 - 7 - 14 - 111629 + 11 + 21 + 113330 - 14 - 29 - 102100 - - - 29 + 21 194 - 59898 + 103726 @@ -7207,7 +7268,7 @@ 1 2 - 9270724 + 10570471 @@ -7217,15 +7278,15 @@ cupackage - 7140227 + 7979236 id - 7140227 + 7979236 packageid - 609880 + 576256 @@ -7239,7 +7300,7 @@ 1 2 - 7140227 + 7979236 @@ -7255,52 +7316,52 @@ 1 2 - 148386 + 121013 2 3 - 80319 + 80675 3 4 - 55814 + 42258 4 - 5 - 42201 + 6 + 49942 - 5 - 7 - 46285 + 6 + 9 + 49942 - 7 - 10 - 50369 + 9 + 12 + 51863 - 10 - 15 - 50369 + 12 + 17 + 48021 - 15 - 21 - 49008 + 17 + 23 + 46100 - 21 - 36 - 47646 + 24 + 43 + 44179 - 39 + 43 187 - 39478 + 42258 @@ -7310,19 +7371,19 @@ jarManifestMain - 172733 + 172742 fileid - 13274 + 13275 keyName - 12610 + 12611 value - 89768 + 89772 @@ -7407,7 +7468,7 @@ 5 6 - 2488 + 2489 6 @@ -7473,7 +7534,7 @@ 1 2 - 5143 + 5144 2 @@ -7580,7 +7641,7 @@ 1 2 - 75996 + 75999 2 @@ -7611,7 +7672,7 @@ 1 2 - 75664 + 75668 2 @@ -7621,7 +7682,7 @@ 3 6 - 5309 + 5310 @@ -7844,7 +7905,7 @@ 1 2 - 30112 + 30113 2 @@ -7886,7 +7947,7 @@ 1 2 - 30124 + 30125 3 @@ -8005,7 +8066,7 @@ 1 2 - 30161 + 30162 2 @@ -8026,7 +8087,7 @@ 1 2 - 30161 + 30162 11 @@ -8062,15 +8123,15 @@ packages - 611241 + 580098 id - 611241 + 580098 nodeName - 611241 + 580098 @@ -8084,7 +8145,7 @@ 1 2 - 611241 + 580098 @@ -8100,7 +8161,7 @@ 1 2 - 611241 + 580098 @@ -8110,15 +8171,15 @@ primitives - 12252 + 17287 id - 12252 + 17287 nodeName - 12252 + 17287 @@ -8132,7 +8193,7 @@ 1 2 - 12252 + 17287 @@ -8148,7 +8209,7 @@ 1 2 - 12252 + 17287 @@ -8158,15 +8219,15 @@ modifiers - 13613 + 26891 id - 13613 + 26891 nodeName - 13613 + 26891 @@ -8180,7 +8241,7 @@ 1 2 - 13613 + 26891 @@ -8196,7 +8257,7 @@ 1 2 - 13613 + 26891 @@ -8217,23 +8278,23 @@ classes - 12548830 + 12618104 id - 12548830 + 12618104 nodeName - 6855707 + 6732600 parentid - 442435 + 455242 sourceid - 4506034 + 5253541 @@ -8247,7 +8308,7 @@ 1 2 - 12548830 + 12618104 @@ -8263,7 +8324,7 @@ 1 2 - 12548830 + 12618104 @@ -8279,7 +8340,7 @@ 1 2 - 12548830 + 12618104 @@ -8295,17 +8356,17 @@ 1 2 - 5728517 + 5601216 2 3 - 741930 + 726083 3 - 236 - 385259 + 204 + 405300 @@ -8321,17 +8382,17 @@ 1 2 - 6243104 + 5950812 2 3 - 544535 + 731846 3 52 - 68066 + 49942 @@ -8347,17 +8408,17 @@ 1 2 - 6219961 + 5921999 2 3 - 533645 + 714558 3 160 - 102100 + 96042 @@ -8373,57 +8434,62 @@ 1 2 - 107545 + 94121 2 3 - 54453 + 46100 3 4 - 32672 + 30733 4 5 - 29949 + 26891 5 - 7 - 34033 + 6 + 24971 - 7 - 11 - 40840 + 6 + 8 + 40337 - 11 - 17 - 34033 + 8 + 12 + 32654 - 17 + 12 + 18 + 38417 + + + 18 23 - 34033 + 34575 23 40 - 36756 + 36496 40 - 707 - 34033 + 76 + 34575 - 1013 - 1414 - 4084 + 83 + 891 + 15366 @@ -8439,52 +8505,57 @@ 1 2 - 107545 + 96042 2 3 - 54453 + 46100 3 4 - 35394 + 34575 4 5 - 34033 + 28812 5 - 7 - 38117 + 6 + 36496 - 7 - 11 - 40840 + 6 + 9 + 40337 - 11 - 17 - 36756 + 9 + 13 + 40337 - 17 - 23 - 34033 + 13 + 18 + 26891 - 23 - 40 - 35394 + 18 + 25 + 38417 - 40 - 830 - 25865 + 26 + 41 + 36496 + + + 41 + 479 + 30733 @@ -8500,52 +8571,57 @@ 1 2 - 118436 + 99884 2 3 - 63982 + 57625 3 4 - 36756 + 36496 4 5 - 34033 + 32654 5 - 7 - 35394 + 6 + 32654 - 7 - 11 - 40840 + 6 + 8 + 34575 - 11 - 17 - 34033 + 8 + 12 + 38417 - 17 - 26 - 34033 + 12 + 18 + 32654 - 26 - 56 - 34033 + 18 + 25 + 34575 - 64 + 25 + 47 + 34575 + + + 51 138 - 10890 + 21129 @@ -8561,17 +8637,17 @@ 1 2 - 4040456 + 4694572 2 11 - 341696 + 407221 11 - 1358 - 123881 + 426 + 151747 @@ -8587,17 +8663,17 @@ 1 2 - 4040456 + 4694572 2 6 - 359393 + 426430 6 - 783 - 106184 + 224 + 132539 @@ -8613,7 +8689,7 @@ 1 2 - 4506034 + 5253541 @@ -8623,26 +8699,26 @@ file_class - 14974 + 17287 id - 14974 + 17287 class_object - 122520 + 163272 id - 122520 + 163272 instance - 122520 + 163272 @@ -8656,7 +8732,7 @@ 1 2 - 122520 + 163272 @@ -8672,7 +8748,7 @@ 1 2 - 122520 + 163272 @@ -8682,19 +8758,19 @@ type_companion_object - 217814 + 307336 id - 217814 + 307336 instance - 217814 + 307336 companion_object - 217814 + 307336 @@ -8708,7 +8784,7 @@ 1 2 - 217814 + 307336 @@ -8724,7 +8800,7 @@ 1 2 - 217814 + 307336 @@ -8740,7 +8816,7 @@ 1 2 - 217814 + 307336 @@ -8756,7 +8832,7 @@ 1 2 - 217814 + 307336 @@ -8772,7 +8848,7 @@ 1 2 - 217814 + 307336 @@ -8788,7 +8864,7 @@ 1 2 - 217814 + 307336 @@ -8798,15 +8874,15 @@ kt_nullable_types - 1361 + 1920 id - 1361 + 1920 classid - 1361 + 1920 @@ -8820,7 +8896,7 @@ 1 2 - 1361 + 1920 @@ -8836,7 +8912,7 @@ 1 2 - 1361 + 1920 @@ -8846,15 +8922,15 @@ kt_notnull_types - 191419 + 172391 id - 191419 + 172391 classid - 191419 + 172391 @@ -8868,7 +8944,7 @@ 1 2 - 191419 + 172391 @@ -8884,7 +8960,7 @@ 1 2 - 191419 + 172391 @@ -8894,19 +8970,19 @@ kt_type_alias - 2060 + 1821 id - 2060 + 1821 name - 2060 + 1821 kttypeid - 1030 + 910 @@ -8920,7 +8996,7 @@ 1 2 - 2060 + 1821 @@ -8936,7 +9012,7 @@ 1 2 - 2060 + 1821 @@ -8952,7 +9028,7 @@ 1 2 - 2060 + 1821 @@ -8968,7 +9044,7 @@ 1 2 - 2060 + 1821 @@ -8984,7 +9060,7 @@ 2 3 - 1030 + 910 @@ -9000,7 +9076,7 @@ 2 3 - 1030 + 910 @@ -9021,23 +9097,23 @@ interfaces - 21501109 + 23200100 id - 21501109 + 23200100 nodeName - 5448969 + 8705320 parentid - 350 + 430271 sourceid - 2074 + 2787162 @@ -9051,7 +9127,7 @@ 1 2 - 21501109 + 23200100 @@ -9067,7 +9143,7 @@ 1 2 - 21501109 + 23200100 @@ -9083,7 +9159,7 @@ 1 2 - 21501109 + 23200100 @@ -9099,27 +9175,22 @@ 1 2 - 3495545 + 6834406 2 3 - 811460 + 1079521 3 - 5 - 410487 + 25 + 655011 - 5 - 12 - 426139 - - - 12 - 9655 - 305336 + 25 + 345 + 136380 @@ -9135,231 +9206,221 @@ 1 2 - 5448745 + 7996524 2 - 3 - 224 - - - - - - - nodeName - sourceid - - - 12 - - - 1 - 2 - 5448653 - - - 2 - 6 - 316 - - - - - - - parentid - id - - - 12 - - - 1 - 2 - 57 - - - 2 - 3 - 34 - - - 3 4 - 28 + 695349 4 - 6 - 28 - - - 6 - 9 - 28 - - - 10 - 16 - 28 - - - 16 - 20 - 28 - - - 21 - 212 - 28 - - - 288 - 1113 - 28 - - - 1315 - 27761 - 28 - - - 36093 - 2073873 - 28 - - - - - - - parentid - nodeName - - - 12 - - - 1 - 2 - 57 - - - 2 - 3 - 34 - - - 3 - 4 - 28 - - - 4 - 5 - 5 - - - 5 - 6 - 28 - - - 6 - 11 - 28 - - - 11 - 14 - 28 - - - 15 - 20 - 28 - - - 21 - 107 - 28 - - - 153 - 381 - 28 - - - 3060 - 15096 - 28 - - - 36080 - 500307 - 22 - - - - - - - parentid - sourceid - - - 12 - - - 1 - 2 - 97 - - - 2 - 3 - 40 - - - 3 - 4 - 34 - - - 4 - 6 - 28 - - - 6 7 - 11 + 13445 + + + + + + + nodeName + sourceid + + + 12 + + + 1 + 2 + 7967711 - 7 + 2 + 4 + 718400 + + + 4 + 7 + 19208 + + + + + + + parentid + id + + + 12 + + + 1 + 2 + 113330 + + + 2 + 3 + 55704 + + + 3 + 4 + 48021 + + + 4 + 5 + 26891 + + + 5 8 - 40 + 36496 8 + 13 + 34575 + + + 13 + 18 + 34575 + + + 18 + 41 + 32654 + + + 42 + 182 + 32654 + + + 187 + 4152 + 15366 + + + + + + + parentid + nodeName + + + 12 + + + 1 + 2 + 113330 + + + 2 + 3 + 55704 + + + 3 + 4 + 48021 + + + 4 + 5 + 38417 + + + 5 + 7 + 24971 + + + 7 10 - 22 + 32654 10 - 11 - 28 - - - 11 15 - 28 + 36496 - 19 - 40 - 17 + 15 + 28 + 32654 + + + 30 + 94 + 32654 + + + 100 + 1213 + 15366 + + + + + + + parentid + sourceid + + + 12 + + + 1 + 2 + 140222 + + + 2 + 3 + 65309 + + + 3 + 4 + 44179 + + + 4 + 5 + 34575 + + + 5 + 7 + 28812 + + + 7 + 9 + 34575 + + + 9 + 14 + 34575 + + + 14 + 26 + 32654 + + + 26 + 160 + 15366 @@ -9375,32 +9436,17 @@ 1 2 - 1292 + 2391465 2 - 7 - 166 + 11 + 213215 - 7 - 53 - 160 - - - 55 - 289 - 166 - - - 353 - 3260 - 160 - - - 3665 - 450913 - 126 + 11 + 1565 + 182481 @@ -9416,32 +9462,17 @@ 1 2 - 1292 + 2391465 2 6 - 172 + 217056 6 - 31 - 160 - - - 31 - 110 - 160 - - - 115 - 1202 - 160 - - - 1336 - 101026 - 126 + 492 + 178639 @@ -9457,7 +9488,7 @@ 1 2 - 2074 + 2787162 @@ -9467,15 +9498,15 @@ fielddecls - 398872 + 199475 id - 398872 + 199475 parentid - 59898 + 25503 @@ -9489,7 +9520,7 @@ 1 2 - 398872 + 199475 @@ -9505,32 +9536,57 @@ 1 2 - 29949 + 4554 2 3 - 13613 + 5465 3 4 - 5445 + 1821 4 - 5 - 2722 + 6 + 1821 6 - 16 - 5445 + 7 + 2732 - 40 - 159 - 2722 + 7 + 9 + 1821 + + + 9 + 10 + 910 + + + 10 + 11 + 1821 + + + 13 + 18 + 1821 + + + 19 + 20 + 1821 + + + 57 + 58 + 910 @@ -9540,19 +9596,19 @@ fieldDeclaredIn - 398872 + 199475 fieldId - 398872 + 199475 fieldDeclId - 398872 + 199475 pos - 1361 + 910 @@ -9566,7 +9622,7 @@ 1 2 - 398872 + 199475 @@ -9582,7 +9638,7 @@ 1 2 - 398872 + 199475 @@ -9598,7 +9654,7 @@ 1 2 - 398872 + 199475 @@ -9614,7 +9670,7 @@ 1 2 - 398872 + 199475 @@ -9628,9 +9684,9 @@ 12 - 293 - 294 - 1361 + 219 + 220 + 910 @@ -9644,9 +9700,9 @@ 12 - 293 - 294 - 1361 + 219 + 220 + 910 @@ -9656,27 +9712,27 @@ fields - 27509955 + 19350704 id - 27509955 + 19350704 nodeName - 10900247 + 13628474 typeid - 2728125 + 3038794 parentid - 3851230 + 4089502 sourceid - 27509955 + 19350704 @@ -9690,7 +9746,7 @@ 1 2 - 27509955 + 19350704 @@ -9706,7 +9762,7 @@ 1 2 - 27509955 + 19350704 @@ -9722,7 +9778,7 @@ 1 2 - 27509955 + 19350704 @@ -9738,7 +9794,7 @@ 1 2 - 27509955 + 19350704 @@ -9754,22 +9810,17 @@ 1 2 - 8061854 + 12016876 2 3 - 1433490 + 1123700 3 - 11 - 760988 - - - 11 - 828 - 643913 + 722 + 487897 @@ -9785,17 +9836,17 @@ 1 2 - 9875159 + 12416414 2 - 4 - 850837 + 5 + 1085283 - 4 + 5 160 - 174251 + 126776 @@ -9811,22 +9862,17 @@ 1 2 - 8061854 + 12016876 2 3 - 1433490 + 1123700 3 - 11 - 760988 - - - 11 - 828 - 643913 + 722 + 487897 @@ -9842,22 +9888,17 @@ 1 2 - 8061854 + 12016876 2 3 - 1433490 + 1123700 3 - 11 - 760988 - - - 11 - 828 - 643913 + 722 + 487897 @@ -9873,32 +9914,27 @@ 1 2 - 1732985 + 2059157 2 3 - 338973 + 320782 3 4 - 175612 + 213215 4 - 7 - 208284 + 8 + 245869 - 7 - 24 - 205562 - - - 24 - 7479 - 66705 + 8 + 2588 + 199769 @@ -9914,27 +9950,27 @@ 1 2 - 1894985 + 2120625 2 3 - 302217 + 299653 3 4 - 183780 + 199769 4 9 - 212369 + 255473 9 - 2335 - 134772 + 2016 + 163272 @@ -9950,17 +9986,17 @@ 1 2 - 2326529 + 2706486 2 4 - 228705 + 251632 4 - 1392 - 172890 + 1014 + 80675 @@ -9976,32 +10012,27 @@ 1 2 - 1732985 + 2059157 2 3 - 338973 + 320782 3 4 - 175612 + 213215 4 - 7 - 208284 + 8 + 245869 - 7 - 24 - 205562 - - - 24 - 7479 - 66705 + 8 + 2588 + 199769 @@ -10017,37 +10048,32 @@ 1 2 - 1937186 + 2427962 2 3 - 484636 + 478293 3 4 - 303578 + 303495 4 6 - 341696 + 338070 6 - 10 - 300856 + 13 + 330387 - 10 - 27 - 291326 - - - 27 + 13 1610 - 191948 + 211294 @@ -10063,37 +10089,32 @@ 1 2 - 1937186 + 2427962 2 3 - 484636 + 478293 3 4 - 303578 + 303495 4 6 - 341696 + 338070 6 - 10 - 300856 + 13 + 330387 - 10 - 27 - 291326 - - - 27 + 13 1610 - 191948 + 211294 @@ -10109,27 +10130,22 @@ 1 2 - 2499419 + 3031110 2 3 - 623493 + 595465 3 - 4 - 243679 + 5 + 347674 - 4 - 7 - 338973 - - - 7 + 5 76 - 145663 + 115251 @@ -10145,37 +10161,32 @@ 1 2 - 1937186 + 2427962 2 3 - 484636 + 478293 3 4 - 303578 + 303495 4 6 - 341696 + 338070 6 - 10 - 300856 + 13 + 330387 - 10 - 27 - 291326 - - - 27 + 13 1610 - 191948 + 211294 @@ -10191,7 +10202,7 @@ 1 2 - 27509955 + 19350704 @@ -10207,7 +10218,7 @@ 1 2 - 27509955 + 19350704 @@ -10223,7 +10234,7 @@ 1 2 - 27509955 + 19350704 @@ -10239,7 +10250,7 @@ 1 2 - 27509955 + 19350704 @@ -10249,15 +10260,15 @@ fieldsKotlinType - 27509955 + 19350704 id - 27509955 + 19350704 kttypeid - 1361 + 1920 @@ -10271,7 +10282,7 @@ 1 2 - 27509955 + 19350704 @@ -10285,9 +10296,9 @@ 12 - 20208 - 20209 - 1361 + 10074 + 10075 + 1920 @@ -10297,31 +10308,31 @@ constrs - 6869320 + 8006128 id - 6869320 + 8006128 nodeName - 3746407 + 4333451 signature - 5871458 + 6803672 typeid - 2722 + 3841 parentid - 4835479 + 5639633 sourceid - 5054654 + 5720309 @@ -10335,7 +10346,7 @@ 1 2 - 6869320 + 8006128 @@ -10351,7 +10362,7 @@ 1 2 - 6869320 + 8006128 @@ -10367,7 +10378,7 @@ 1 2 - 6869320 + 8006128 @@ -10383,7 +10394,7 @@ 1 2 - 6869320 + 8006128 @@ -10399,7 +10410,7 @@ 1 2 - 6869320 + 8006128 @@ -10415,22 +10426,58 @@ 1 2 - 2361924 + 2623889 2 3 - 835862 + 1085283 + + + 3 + 4 + 259315 + + + 4 + 11 + 326545 + + + 11 + 36 + 38417 + + + + + + + nodeName + signature + + + 12 + + + 1 + 2 + 2977327 + + + 2 + 3 + 868226 3 5 - 345780 + 341912 5 - 42 - 202839 + 19 + 145985 @@ -10438,7 +10485,7 @@ nodeName - signature + typeid 12 @@ -10446,95 +10493,64 @@ 1 2 - 2628747 + 4333451 + + + + + + + nodeName + parentid + + + 12 + + + 1 + 2 + 3764878 2 3 - 683392 + 401458 + + + 3 + 36 + 167114 + + + + + + + nodeName + sourceid + + + 12 + + + 1 + 2 + 2737220 + + + 2 + 3 + 1083362 3 5 - 302217 + 353437 5 - 19 - 132049 - - - - - - - nodeName - typeid - - - 12 - - - 1 - 2 - 3746407 - - - - - - - nodeName - parentid - - - 12 - - - 1 - 2 - 3287635 - - - 2 - 3 - 314469 - - - 3 - 42 - 144302 - - - - - - - nodeName - sourceid - - - 12 - - - 1 - 2 - 2453134 - - - 2 - 3 - 827694 - - - 3 - 5 - 318553 - - - 5 - 38 - 147024 + 32 + 159431 @@ -10550,12 +10566,12 @@ 1 2 - 5461695 + 6302329 2 - 42 - 409763 + 36 + 501343 @@ -10571,7 +10587,7 @@ 1 2 - 5871458 + 6803672 @@ -10587,7 +10603,7 @@ 1 2 - 5871458 + 6803672 @@ -10603,12 +10619,12 @@ 1 2 - 5461695 + 6302329 2 - 42 - 409763 + 36 + 501343 @@ -10624,12 +10640,12 @@ 1 2 - 5591022 + 6461760 2 - 32 - 280436 + 23 + 341912 @@ -10643,14 +10659,14 @@ 12 - 31 - 32 - 1361 + 22 + 23 + 1920 - 5015 - 5016 - 1361 + 4146 + 4147 + 1920 @@ -10666,12 +10682,12 @@ 1 2 - 1361 + 1920 - 2751 - 2752 - 1361 + 2255 + 2256 + 1920 @@ -10687,12 +10703,12 @@ 1 2 - 1361 + 1920 - 4312 - 4313 - 1361 + 3541 + 3542 + 1920 @@ -10706,14 +10722,14 @@ 12 - 31 - 32 - 1361 + 22 + 23 + 1920 - 3521 - 3522 - 1361 + 2914 + 2915 + 1920 @@ -10727,14 +10743,14 @@ 12 - 31 - 32 - 1361 + 22 + 23 + 1920 - 3682 - 3683 - 1361 + 2956 + 2957 + 1920 @@ -10750,22 +10766,22 @@ 1 2 - 3676978 + 4256617 2 3 - 737846 + 895118 3 6 - 366200 + 434113 6 19 - 54453 + 53783 @@ -10781,7 +10797,7 @@ 1 2 - 4835479 + 5639633 @@ -10797,22 +10813,22 @@ 1 2 - 3676978 + 4256617 2 3 - 737846 + 895118 3 6 - 366200 + 434113 6 19 - 54453 + 53783 @@ -10828,7 +10844,7 @@ 1 2 - 4835479 + 5639633 @@ -10844,22 +10860,22 @@ 1 2 - 3676978 + 4256617 2 3 - 737846 + 895118 3 6 - 366200 + 434113 6 19 - 54453 + 53783 @@ -10875,12 +10891,12 @@ 1 2 - 4742907 + 5357267 2 - 259 - 311746 + 209 + 363041 @@ -10896,12 +10912,12 @@ 1 2 - 4742907 + 5357267 2 - 212 - 311746 + 172 + 363041 @@ -10917,12 +10933,12 @@ 1 2 - 4742907 + 5357267 2 - 212 - 311746 + 172 + 363041 @@ -10938,7 +10954,7 @@ 1 2 - 5054654 + 5720309 @@ -10954,12 +10970,12 @@ 1 2 - 4742907 + 5357267 2 - 259 - 311746 + 209 + 363041 @@ -10969,15 +10985,15 @@ constrsKotlinType - 6869320 + 8006128 id - 6869320 + 8006128 kttypeid - 1361 + 1920 @@ -10991,7 +11007,7 @@ 1 2 - 6869320 + 8006128 @@ -11005,9 +11021,9 @@ 12 - 5046 - 5047 - 1361 + 4168 + 4169 + 1920 @@ -11017,31 +11033,31 @@ methods - 93308954 + 109719302 id - 93308954 + 109719302 nodeName - 20326164 + 22908130 signature - 29772501 + 33369112 typeid - 11583640 + 13338425 parentid - 11270532 + 12810189 sourceid - 58543057 + 67091663 @@ -11055,7 +11071,7 @@ 1 2 - 93308954 + 109719302 @@ -11071,7 +11087,7 @@ 1 2 - 93308954 + 109719302 @@ -11087,7 +11103,7 @@ 1 2 - 93308954 + 109719302 @@ -11103,7 +11119,7 @@ 1 2 - 93308954 + 109719302 @@ -11119,7 +11135,7 @@ 1 2 - 93308954 + 109719302 @@ -11135,32 +11151,32 @@ 1 2 - 11834127 + 13324979 2 3 - 3821280 + 4070294 3 4 - 1315054 + 1573181 4 7 - 1792884 + 2026503 7 - 271 - 1524700 + 56 + 1719166 - 276 - 2134 - 38117 + 57 + 1769 + 194006 @@ -11176,17 +11192,17 @@ 1 2 - 16853387 + 19150935 2 3 - 2115522 + 2228193 3 - 361 - 1357255 + 298 + 1529001 @@ -11202,22 +11218,22 @@ 1 2 - 17082092 + 19179748 2 3 - 1678532 + 1872834 3 - 52 - 1524700 + 25 + 1726849 - 52 - 845 - 40840 + 25 + 741 + 128697 @@ -11233,27 +11249,27 @@ 1 2 - 12555637 + 14028012 2 3 - 3596659 + 3889733 3 4 - 1245625 + 1502109 4 7 - 1577792 + 1817129 7 - 1278 - 1350449 + 1099 + 1671144 @@ -11269,27 +11285,27 @@ 1 2 - 12213940 + 13743725 2 3 - 3951969 + 4283509 3 4 - 1297356 + 1544368 4 7 - 1656750 + 1857467 7 - 928 - 1206147 + 800 + 1479059 @@ -11305,22 +11321,27 @@ 1 2 - 20356114 + 22735253 2 3 - 4957999 + 5434102 3 5 - 2363285 + 2635414 5 - 1275 - 2095101 + 157 + 2502875 + + + 157 + 1096 + 61467 @@ -11336,7 +11357,7 @@ 1 2 - 29772501 + 33369112 @@ -11352,17 +11373,17 @@ 1 2 - 26733991 + 29957672 2 5 - 2383706 + 2650781 5 - 843 - 654804 + 739 + 760659 @@ -11378,22 +11399,27 @@ 1 2 - 20360198 + 22741015 2 3 - 4956638 + 5432181 3 5 - 2361924 + 2631572 5 - 1275 - 2093740 + 157 + 2502875 + + + 157 + 1096 + 61467 @@ -11409,22 +11435,22 @@ 1 2 - 20978246 + 23365294 2 3 - 5076436 + 5656921 3 6 - 2533453 + 2900492 6 - 923 - 1184365 + 795 + 1446404 @@ -11440,32 +11466,32 @@ 1 2 - 5684955 + 6603903 2 3 - 2449050 + 2729536 3 4 - 1089071 + 1208218 4 6 - 925711 + 1119859 6 - 14 - 875341 + 15 + 1019974 - 14 - 11667 - 559510 + 15 + 10335 + 656932 @@ -11481,22 +11507,22 @@ 1 2 - 7613973 + 8714924 2 3 - 2218983 + 2620047 3 6 - 1060483 + 1167880 6 - 3940 - 690199 + 2888 + 835572 @@ -11512,27 +11538,22 @@ 1 2 - 7370293 + 8565097 2 3 - 2274798 + 2602760 3 - 5 - 875341 + 6 + 1210139 - 5 - 17 - 882148 - - - 17 - 5689 - 181058 + 6 + 4142 + 960428 @@ -11548,27 +11569,27 @@ 1 2 - 6786279 + 7714158 2 3 - 2466747 + 2869759 3 4 - 1003307 + 1108333 4 - 9 - 916181 + 8 + 1069916 - 9 - 3420 - 411124 + 8 + 2865 + 576256 @@ -11584,32 +11605,32 @@ 1 2 - 6180482 + 7166714 2 3 - 2262546 + 2524004 3 4 - 985610 + 1096808 4 6 - 910736 + 1094888 6 16 - 890316 + 1016132 16 - 8855 - 353948 + 6435 + 439876 @@ -11625,52 +11646,52 @@ 1 2 - 3190980 + 3630418 2 3 - 1287827 + 1456008 3 4 - 1029172 + 1164038 4 5 - 744652 + 735687 5 7 - 924349 + 1021895 7 10 - 952937 + 1106413 10 13 - 997862 + 1146751 13 - 19 - 908013 + 21 + 1181326 - 19 - 38 - 909375 + 21 + 40 + 1021895 - 38 + 40 319 - 325360 + 345754 @@ -11686,52 +11707,52 @@ 1 2 - 3244072 + 3678439 2 3 - 1308247 + 1457929 3 4 - 1109491 + 1231268 4 5 - 785493 + 791392 5 7 - 887593 + 960428 7 10 - 1015559 + 1183247 10 13 - 942047 + 1102571 13 - 17 - 875341 + 18 + 849018 - 17 - 35 - 848114 + 18 + 26 + 968111 - 35 + 26 290 - 254570 + 587781 @@ -11747,52 +11768,52 @@ 1 2 - 3190980 + 3630418 2 3 - 1287827 + 1456008 3 4 - 1029172 + 1164038 4 5 - 744652 + 735687 5 7 - 924349 + 1021895 7 10 - 952937 + 1106413 10 13 - 997862 + 1146751 13 - 19 - 908013 + 21 + 1181326 - 19 - 38 - 909375 + 21 + 40 + 1021895 - 38 + 40 319 - 325360 + 345754 @@ -11808,47 +11829,52 @@ 1 2 - 3887986 + 4348818 2 3 - 1615910 + 1757583 3 4 - 1327306 + 1436800 4 5 - 786854 + 922010 5 6 - 637107 + 743371 6 7 - 498250 + 564731 7 - 9 - 1033256 + 8 + 879752 - 9 - 13 - 882148 + 8 + 11 + 1160197 - 13 + 11 + 31 + 964269 + + + 33 78 - 601712 + 32654 @@ -11864,52 +11890,52 @@ 1 2 - 3190980 + 3630418 2 3 - 1287827 + 1456008 3 4 - 1029172 + 1164038 4 5 - 744652 + 735687 5 7 - 924349 + 1021895 7 10 - 952937 + 1106413 10 13 - 997862 + 1146751 13 - 19 - 908013 + 21 + 1181326 - 19 - 38 - 909375 + 21 + 40 + 1021895 - 38 + 40 319 - 325360 + 345754 @@ -11925,12 +11951,17 @@ 1 2 - 54190854 + 61836200 2 - 349 - 4352203 + 50 + 5032643 + + + 52 + 286 + 222819 @@ -11946,7 +11977,7 @@ 1 2 - 58543057 + 67091663 @@ -11962,12 +11993,12 @@ 1 2 - 58259899 + 66767038 2 - 347 - 283158 + 284 + 324624 @@ -11983,12 +12014,12 @@ 1 2 - 56848189 + 65030584 2 - 259 - 1694868 + 209 + 2061078 @@ -12004,12 +12035,17 @@ 1 2 - 54190854 + 61836200 2 - 349 - 4352203 + 50 + 5032643 + + + 52 + 286 + 222819 @@ -12019,15 +12055,15 @@ methodsKotlinType - 93308954 + 109719302 id - 93308954 + 109719302 kttypeid - 1361 + 1920 @@ -12041,7 +12077,7 @@ 1 2 - 93308954 + 109719302 @@ -12055,9 +12091,9 @@ 12 - 68542 - 68543 - 1361 + 57120 + 57121 + 1920 @@ -12067,27 +12103,27 @@ params - 101015498 + 120852585 id - 101015498 + 120852585 typeid - 11171154 + 12833239 pos - 29949 + 42258 parentid - 56251922 + 65007534 sourceid - 61310661 + 73639861 @@ -12101,7 +12137,7 @@ 1 2 - 101015498 + 120852585 @@ -12117,7 +12153,7 @@ 1 2 - 101015498 + 120852585 @@ -12133,7 +12169,7 @@ 1 2 - 101015498 + 120852585 @@ -12149,7 +12185,7 @@ 1 2 - 101015498 + 120852585 @@ -12165,32 +12201,37 @@ 1 2 - 5943609 + 6763334 2 3 - 1712565 + 1945827 3 4 - 869896 + 948902 4 6 - 970635 + 1148671 6 12 - 867173 + 1018053 12 - 7469 - 807274 + 326 + 966190 + + + 343 + 6738 + 42258 @@ -12206,17 +12247,22 @@ 1 2 - 9232606 + 10436011 2 3 - 1157138 + 1342678 3 + 8 + 964269 + + + 8 17 - 781409 + 90280 @@ -12232,32 +12278,32 @@ 1 2 - 6134197 + 7005362 2 3 - 1677170 + 1895885 3 4 - 888954 + 975794 4 6 - 917543 + 1087204 6 13 - 867173 + 1008449 13 - 5265 - 686115 + 4326 + 860543 @@ -12273,32 +12319,32 @@ 1 2 - 6337036 + 7216656 2 3 - 1569624 + 1788317 3 4 - 901206 + 996924 4 6 - 947492 + 1117938 6 13 - 856282 + 1002686 13 - 6292 - 559510 + 5757 + 710716 @@ -12314,57 +12360,57 @@ 1 2 - 2722 + 3841 - 53 - 56 - 2722 + 50 + 53 + 3841 - 110 - 112 - 2722 + 104 + 106 + 3841 - 165 - 172 - 2722 + 157 + 165 + 3841 - 224 - 242 - 2722 + 214 + 231 + 3841 - 305 - 330 - 2722 + 291 + 315 + 3841 - 524 - 666 - 2722 + 485 + 606 + 3841 - 880 - 1142 - 2722 + 804 + 1067 + 3841 - 1517 - 2090 - 2722 + 1445 + 2015 + 3841 - 3299 - 5949 - 2722 + 3084 + 5199 + 3841 - 15053 - 41322 - 2722 + 12689 + 33844 + 3841 @@ -12380,57 +12426,57 @@ 1 2 - 2722 + 3841 2 5 - 2722 + 3841 6 7 - 2722 + 3841 - 10 - 12 - 2722 + 11 + 14 + 3841 - 13 - 19 - 2722 + 15 + 20 + 3841 - 26 - 38 - 2722 + 27 + 37 + 3841 57 - 76 - 2722 + 75 + 3841 - 99 - 159 - 2722 + 97 + 153 + 3841 - 212 - 306 - 2722 + 201 + 289 + 3841 - 452 - 889 - 2722 + 403 + 777 + 3841 - 2370 - 6264 - 2722 + 1979 + 5089 + 3841 @@ -12446,57 +12492,57 @@ 1 2 - 2722 + 3841 - 53 - 56 - 2722 + 50 + 53 + 3841 - 110 - 112 - 2722 + 104 + 106 + 3841 - 165 - 172 - 2722 + 157 + 165 + 3841 - 224 - 242 - 2722 + 214 + 231 + 3841 - 305 - 330 - 2722 + 291 + 315 + 3841 - 524 - 666 - 2722 + 485 + 606 + 3841 - 880 - 1142 - 2722 + 804 + 1067 + 3841 - 1517 - 2090 - 2722 + 1445 + 2015 + 3841 - 3299 - 5949 - 2722 + 3084 + 5199 + 3841 - 15053 - 41322 - 2722 + 12689 + 33844 + 3841 @@ -12512,57 +12558,57 @@ 1 2 - 2722 + 3841 2 5 - 2722 + 3841 6 8 - 2722 + 3841 - 10 - 13 - 2722 + 11 + 15 + 3841 - 14 - 28 - 2722 + 16 + 29 + 3841 - 38 + 39 63 - 2722 + 3841 - 98 - 138 - 2722 + 101 + 144 + 3841 - 193 - 344 - 2722 + 210 + 386 + 3841 - 556 - 966 - 2722 + 628 + 1069 + 3841 - 1954 - 4172 - 2722 + 1955 + 3748 + 3841 - 10056 - 26381 - 2722 + 8555 + 21355 + 3841 @@ -12578,27 +12624,27 @@ 1 2 - 35759674 + 40633790 2 3 - 12394999 + 14389133 3 4 - 3606189 + 4060689 4 - 15 - 4258270 + 10 + 4992305 - 15 + 10 23 - 232789 + 931615 @@ -12614,17 +12660,17 @@ 1 2 - 39751122 + 45326442 2 3 - 12655015 + 14805959 3 23 - 3845785 + 4875132 @@ -12640,27 +12686,27 @@ 1 2 - 35759674 + 40633790 2 3 - 12394999 + 14389133 3 4 - 3606189 + 4060689 4 - 15 - 4258270 + 10 + 4992305 - 15 + 10 23 - 232789 + 931615 @@ -12676,27 +12722,27 @@ 1 2 - 35759674 + 40633790 2 3 - 12394999 + 14389133 3 4 - 3606189 + 4060689 4 - 15 - 4258270 + 10 + 4992305 - 15 + 10 23 - 232789 + 931615 @@ -12712,12 +12758,12 @@ 1 2 - 56891752 + 68584168 2 - 349 - 4418909 + 286 + 5055693 @@ -12733,12 +12779,12 @@ 1 2 - 59772347 + 71932220 2 - 349 - 1538314 + 286 + 1707641 @@ -12754,7 +12800,7 @@ 1 2 - 61310661 + 73639861 @@ -12770,12 +12816,12 @@ 1 2 - 56891752 + 68584168 2 - 349 - 4418909 + 286 + 5055693 @@ -12785,15 +12831,15 @@ paramsKotlinType - 101015498 + 120852585 id - 101015498 + 120852585 kttypeid - 1361 + 1920 @@ -12807,7 +12853,7 @@ 1 2 - 101015498 + 120852585 @@ -12821,9 +12867,9 @@ 12 - 74203 - 74204 - 1361 + 62916 + 62917 + 1920 @@ -12833,15 +12879,15 @@ paramName - 10331207 + 15639610 id - 10331207 + 15639610 nodeName - 1662195 + 2335761 @@ -12855,7 +12901,7 @@ 1 2 - 10331207 + 15639610 @@ -12871,37 +12917,37 @@ 1 2 - 717426 + 998845 2 3 - 328082 + 451401 3 4 - 174251 + 249711 4 5 - 129327 + 170956 5 - 9 - 137495 + 8 + 182481 - 9 - 25 - 130688 + 8 + 18 + 176718 - 25 - 517 - 44924 + 18 + 769 + 105647 @@ -12911,30 +12957,30 @@ isVarargsParam - 1003307 + 945061 param - 1003307 + 945061 exceptions - 1228169 + 1232644 id - 1228169 + 1232644 typeid - 36993 + 36990 parentid - 982877 + 987367 @@ -12948,7 +12994,7 @@ 1 2 - 1228169 + 1232644 @@ -12964,7 +13010,7 @@ 1 2 - 1228169 + 1232644 @@ -12980,7 +13026,7 @@ 1 2 - 11951 + 11950 2 @@ -13010,17 +13056,17 @@ 20 35 - 3414 - - - 49 - 107 2845 - 187 + 41 + 93 + 2845 + + + 106 813 - 1707 + 2276 @@ -13036,7 +13082,7 @@ 1 2 - 11951 + 11950 2 @@ -13066,17 +13112,17 @@ 20 35 - 3414 - - - 49 - 107 2845 - 187 + 41 + 93 + 2845 + + + 106 813 - 1707 + 2276 @@ -13092,12 +13138,12 @@ 1 2 - 750674 + 755179 2 3 - 224234 + 224220 3 @@ -13118,12 +13164,12 @@ 1 2 - 750674 + 755179 2 3 - 224234 + 224220 3 @@ -13138,41 +13184,41 @@ isAnnotType - 30058 + 29260 interfaceid - 30058 + 29260 isAnnotElem - 61062 + 61065 methodid - 61062 + 61065 annotValue - 1574849 + 1574925 parentid - 568147 + 568174 id2 - 52102 + 52104 value - 1574849 + 1574925 @@ -13186,32 +13232,32 @@ 1 2 - 153486 + 153493 2 3 - 252712 + 252724 3 4 - 48949 + 48951 4 6 - 26548 + 26550 6 7 - 35177 + 35179 7 9 - 46626 + 46628 9 @@ -13232,37 +13278,37 @@ 1 2 - 138054 + 138061 2 3 - 268144 + 268157 3 4 - 47290 + 47292 4 6 - 26051 + 26052 6 8 - 39657 + 39659 8 10 - 44137 + 44139 10 13 - 4811 + 4812 @@ -13278,22 +13324,22 @@ 1 2 - 17090 + 17091 2 3 - 5143 + 5144 3 4 - 2654 + 2655 4 6 - 4811 + 4812 6 @@ -13339,7 +13385,7 @@ 1 2 - 15265 + 15266 2 @@ -13364,7 +13410,7 @@ 8 10 - 4811 + 4812 10 @@ -13400,7 +13446,7 @@ 1 2 - 1574849 + 1574925 @@ -13416,7 +13462,7 @@ 1 2 - 1574849 + 1574925 @@ -13426,49 +13472,49 @@ isEnumType - 349864 + 397617 classid - 349864 + 397617 isEnumConst - 321905 + 3081053 fieldid - 321905 + 3081053 typeVars - 5105024 + 6288883 id - 5105024 + 6288883 nodeName - 70789 + 86438 pos - 5445 + 7683 kind - 1361 + 1920 parentid - 3715096 + 4444861 @@ -13482,7 +13528,7 @@ 1 2 - 5105024 + 6288883 @@ -13498,7 +13544,7 @@ 1 2 - 5105024 + 6288883 @@ -13514,7 +13560,7 @@ 1 2 - 5105024 + 6288883 @@ -13530,7 +13576,7 @@ 1 2 - 5105024 + 6288883 @@ -13546,47 +13592,47 @@ 1 2 - 20420 + 21129 2 3 - 10890 + 11525 3 4 - 6806 + 9604 4 7 - 5445 + 7683 7 10 - 5445 + 7683 10 28 - 5445 + 7683 - 37 - 71 - 5445 + 36 + 69 + 7683 71 - 253 - 5445 + 412 + 7683 - 459 - 951 - 5445 + 603 + 710 + 5762 @@ -13602,22 +13648,22 @@ 1 2 - 44924 + 51863 2 3 - 16336 + 23050 3 4 - 8168 + 9604 4 5 - 1361 + 1920 @@ -13633,7 +13679,7 @@ 1 2 - 70789 + 86438 @@ -13649,47 +13695,47 @@ 1 2 - 20420 + 21129 2 3 - 10890 + 11525 3 4 - 6806 + 9604 4 7 - 5445 + 7683 7 10 - 5445 + 7683 10 28 - 5445 + 7683 - 37 - 71 - 5445 + 36 + 69 + 7683 71 - 253 - 5445 + 412 + 7683 - 459 - 951 - 5445 + 603 + 710 + 5762 @@ -13705,22 +13751,22 @@ 6 7 - 1361 + 1920 - 94 - 95 - 1361 + 89 + 90 + 1920 - 921 - 922 - 1361 + 865 + 866 + 1920 - 2729 - 2730 - 1361 + 2314 + 2315 + 1920 @@ -13736,22 +13782,22 @@ 2 3 - 1361 + 1920 13 14 - 1361 + 1920 - 23 - 24 - 1361 + 21 + 22 + 1920 - 41 - 42 - 1361 + 34 + 35 + 1920 @@ -13767,7 +13813,7 @@ 1 2 - 5445 + 7683 @@ -13783,22 +13829,22 @@ 6 7 - 1361 + 1920 - 94 - 95 - 1361 + 89 + 90 + 1920 - 921 - 922 - 1361 + 865 + 866 + 1920 - 2729 - 2730 - 1361 + 2314 + 2315 + 1920 @@ -13812,9 +13858,9 @@ 12 - 3750 - 3751 - 1361 + 3274 + 3275 + 1920 @@ -13828,9 +13874,9 @@ 12 - 52 - 53 - 1361 + 45 + 46 + 1920 @@ -13846,7 +13892,7 @@ 4 5 - 1361 + 1920 @@ -13860,9 +13906,9 @@ 12 - 2729 - 2730 - 1361 + 2314 + 2315 + 1920 @@ -13878,17 +13924,17 @@ 1 2 - 2461302 + 2783320 2 3 - 1125828 + 1490584 3 5 - 127965 + 170956 @@ -13904,17 +13950,17 @@ 1 2 - 2461302 + 2783320 2 3 - 1125828 + 1490584 3 5 - 127965 + 170956 @@ -13930,17 +13976,17 @@ 1 2 - 2461302 + 2783320 2 3 - 1125828 + 1490584 3 5 - 127965 + 170956 @@ -13956,7 +14002,7 @@ 1 2 - 3715096 + 4444861 @@ -13966,19 +14012,19 @@ wildcards - 3629331 + 3338447 id - 3629331 + 3338447 nodeName - 980164 + 533998 kind - 2722 + 3841 @@ -13992,7 +14038,7 @@ 1 2 - 3629331 + 3338447 @@ -14008,7 +14054,7 @@ 1 2 - 3629331 + 3338447 @@ -14024,17 +14070,22 @@ 1 2 - 789577 + 397617 2 3 - 119797 + 55704 3 - 214 - 70789 + 7 + 44179 + + + 7 + 170 + 36496 @@ -14050,7 +14101,7 @@ 1 2 - 980164 + 533998 @@ -14064,14 +14115,14 @@ 12 - 1027 - 1028 - 1361 + 791 + 792 + 1920 - 1639 - 1640 - 1361 + 947 + 948 + 1920 @@ -14085,14 +14136,14 @@ 12 - 222 - 223 - 1361 + 91 + 92 + 1920 - 498 - 499 - 1361 + 187 + 188 + 1920 @@ -14102,23 +14153,23 @@ typeBounds - 4383514 + 4229725 id - 4383514 + 4229725 typeid - 3184173 + 2852471 pos - 1361 + 1920 parentid - 4383514 + 4229725 @@ -14132,7 +14183,7 @@ 1 2 - 4383514 + 4229725 @@ -14148,7 +14199,7 @@ 1 2 - 4383514 + 4229725 @@ -14164,7 +14215,7 @@ 1 2 - 4383514 + 4229725 @@ -14180,17 +14231,17 @@ 1 2 - 2506226 + 2091812 2 3 - 601712 + 695349 3 52 - 76235 + 65309 @@ -14206,7 +14257,7 @@ 1 2 - 3184173 + 2852471 @@ -14222,17 +14273,17 @@ 1 2 - 2506226 + 2091812 2 3 - 601712 + 695349 3 52 - 76235 + 65309 @@ -14246,9 +14297,9 @@ 12 - 3220 - 3221 - 1361 + 2202 + 2203 + 1920 @@ -14262,9 +14313,9 @@ 12 - 2339 - 2340 - 1361 + 1485 + 1486 + 1920 @@ -14278,9 +14329,9 @@ 12 - 3220 - 3221 - 1361 + 2202 + 2203 + 1920 @@ -14296,7 +14347,7 @@ 1 2 - 4383514 + 4229725 @@ -14312,7 +14363,7 @@ 1 2 - 4383514 + 4229725 @@ -14328,7 +14379,7 @@ 1 2 - 4383514 + 4229725 @@ -14338,11 +14389,11 @@ typeArgs - 53913162 + 53854237 argumentid - 1429292 + 1430551 pos @@ -14350,7 +14401,7 @@ parentid - 22268245 + 22244248 @@ -14364,17 +14415,17 @@ 1 2 - 866127 + 865848 2 3 - 469332 + 470615 3 11 - 93832 + 94087 @@ -14390,57 +14441,57 @@ 1 2 - 62430 + 64071 2 3 - 435425 + 433698 3 5 - 100353 + 100595 5 6 - 35320 + 35547 6 7 - 201035 + 201249 7 11 - 37958 + 39437 11 12 - 218221 + 218178 12 15 - 111144 + 111421 15 29 - 108553 + 108138 29 - 324 - 107243 + 341 + 107450 - 324 - 762773 - 11606 + 341 + 757580 + 10762 @@ -14459,48 +14510,48 @@ 5 - 598 - 599 + 597 + 598 5 - 889 - 890 + 887 + 888 5 - 1132 - 1133 + 1129 + 1130 5 - 1762 - 1763 + 1758 + 1759 5 - 3890 - 3891 + 3885 + 3886 5 - 4033 - 4034 + 4027 + 4028 5 - 51900 - 51901 + 51686 + 51687 5 - 143023 - 143024 + 142042 + 142043 5 - 161995 - 161996 + 161580 + 161581 5 @@ -14525,43 +14576,43 @@ 5 - 3896 - 3897 + 3895 + 3896 5 - 8697 - 8698 + 8694 + 8695 5 - 19018 - 19019 + 19012 + 19013 5 - 85642 - 85643 + 85622 + 85623 5 - 103285 - 103286 + 103257 + 103258 5 - 1458897 - 1458898 + 1447294 + 1447295 5 - 3827033 - 3827034 + 3800219 + 3800220 5 - 3875440 - 3875441 + 3848466 + 3848467 5 @@ -14578,22 +14629,22 @@ 1 2 - 295332 + 296162 2 3 - 13673753 + 13666793 3 4 - 7771091 + 7750153 4 11 - 528068 + 531138 @@ -14609,22 +14660,22 @@ 1 2 - 278146 + 278869 2 3 - 13607289 + 13599976 3 4 - 7789335 + 7768574 4 11 - 593474 + 596828 @@ -14634,37 +14685,37 @@ isParameterized - 25111274 + 27045654 memberid - 25111274 + 27045654 isRaw - 641191 + 731846 memberid - 641191 + 731846 erasure - 25752465 + 27777500 memberid - 25752465 + 27777500 erasureid - 865812 + 954665 @@ -14678,7 +14729,7 @@ 1 2 - 25752465 + 27777500 @@ -14694,57 +14745,62 @@ 1 2 - 144302 + 142143 2 3 - 133411 + 138301 3 4 - 88487 + 86438 4 5 - 54453 + 57625 5 6 - 50369 + 65309 6 8 - 63982 + 69150 8 - 11 - 78957 + 10 + 61467 - 11 - 19 - 70789 + 10 + 15 + 72992 - 19 - 33 - 70789 + 15 + 22 + 76834 - 33 - 93 - 65344 + 22 + 44 + 74913 - 97 - 1848 - 44924 + 46 + 124 + 72992 + + + 169 + 1564 + 36496 @@ -14754,15 +14810,15 @@ isAnonymClass - 192667 + 174213 classid - 192667 + 174213 parent - 192667 + 174213 @@ -14776,7 +14832,7 @@ 1 2 - 192667 + 174213 @@ -14792,7 +14848,7 @@ 1 2 - 192667 + 174213 @@ -14802,15 +14858,15 @@ isLocalClassOrInterface - 4057 + 3841 typeid - 4057 + 3841 parent - 4057 + 3841 @@ -14824,7 +14880,7 @@ 1 2 - 4057 + 3841 @@ -14840,7 +14896,7 @@ 1 2 - 4057 + 3841 @@ -14850,26 +14906,26 @@ isDefConstr - 139100 + 139383 constructorid - 139100 + 139383 lambdaKind - 183936 + 167982 exprId - 183936 + 167982 bodyKind - 15 + 13 @@ -14883,7 +14939,7 @@ 1 2 - 183936 + 167982 @@ -14897,9 +14953,9 @@ 12 - 11651 - 11652 - 15 + 12267 + 12268 + 13 @@ -14909,27 +14965,27 @@ arrays - 1116298 + 1375332 id - 1116298 + 1375332 nodeName - 687476 + 831730 elementtypeid - 1109491 + 1365728 dimension - 2722 + 3841 componenttypeid - 1116298 + 1375332 @@ -14943,7 +14999,7 @@ 1 2 - 1116298 + 1375332 @@ -14959,7 +15015,7 @@ 1 2 - 1116298 + 1375332 @@ -14975,7 +15031,7 @@ 1 2 - 1116298 + 1375332 @@ -14991,7 +15047,7 @@ 1 2 - 1116298 + 1375332 @@ -15007,17 +15063,17 @@ 1 2 - 562233 + 664616 2 3 - 99377 + 140222 3 - 95 - 25865 + 87 + 26891 @@ -15033,17 +15089,17 @@ 1 2 - 562233 + 664616 2 3 - 99377 + 140222 3 - 95 - 25865 + 87 + 26891 @@ -15059,7 +15115,7 @@ 1 2 - 687476 + 831730 @@ -15075,17 +15131,17 @@ 1 2 - 562233 + 664616 2 3 - 99377 + 140222 3 - 95 - 25865 + 87 + 26891 @@ -15101,12 +15157,12 @@ 1 2 - 1102685 + 1356124 2 3 - 6806 + 9604 @@ -15122,12 +15178,12 @@ 1 2 - 1102685 + 1356124 2 3 - 6806 + 9604 @@ -15143,12 +15199,12 @@ 1 2 - 1102685 + 1356124 2 3 - 6806 + 9604 @@ -15164,12 +15220,12 @@ 1 2 - 1102685 + 1356124 2 3 - 6806 + 9604 @@ -15185,12 +15241,12 @@ 5 6 - 1361 + 1920 - 815 - 816 - 1361 + 711 + 712 + 1920 @@ -15206,12 +15262,12 @@ 5 6 - 1361 + 1920 - 500 - 501 - 1361 + 428 + 429 + 1920 @@ -15227,12 +15283,12 @@ 5 6 - 1361 + 1920 - 815 - 816 - 1361 + 711 + 712 + 1920 @@ -15248,12 +15304,12 @@ 5 6 - 1361 + 1920 - 815 - 816 - 1361 + 711 + 712 + 1920 @@ -15269,7 +15325,7 @@ 1 2 - 1116298 + 1375332 @@ -15285,7 +15341,7 @@ 1 2 - 1116298 + 1375332 @@ -15301,7 +15357,7 @@ 1 2 - 1116298 + 1375332 @@ -15317,7 +15373,7 @@ 1 2 - 1116298 + 1375332 @@ -15327,15 +15383,15 @@ enclInReftype - 3410156 + 4051085 child - 3410156 + 4051085 parent - 929795 + 1125621 @@ -15349,7 +15405,7 @@ 1 2 - 3410156 + 4051085 @@ -15365,32 +15421,32 @@ 1 2 - 551342 + 666537 2 3 - 148386 + 176718 3 4 - 70789 + 86438 4 - 7 - 81680 + 6 + 82596 - 7 - 51 - 70789 + 6 + 17 + 84517 - 54 - 279 - 6806 + 17 + 265 + 28812 @@ -15400,15 +15456,15 @@ extendsReftype - 47868792 + 34389087 id1 - 34023966 + 33104034 id2 - 14017716 + 13251986 @@ -15422,22 +15478,12 @@ 1 2 - 26145892 + 32118635 2 - 3 - 3141972 - - - 3 - 4 - 3633415 - - - 4 10 - 1102685 + 985399 @@ -15453,17 +15499,17 @@ 1 2 - 11930782 + 12030322 2 - 3 - 1425322 + 5 + 1023816 - 3 - 12285 - 661611 + 5 + 8715 + 197848 @@ -15473,15 +15519,15 @@ implInterface - 3048147 + 14423708 id1 - 756277 + 8255839 id2 - 2271707 + 4487119 @@ -15495,37 +15541,27 @@ 1 2 - 209615 + 4223962 2 3 - 50204 + 2829421 3 4 - 67386 + 276603 4 5 - 61962 + 920090 5 - 6 - 19294 - - - 6 7 - 305429 - - - 7 - 10 - 42384 + 5762 @@ -15541,12 +15577,17 @@ 1 2 - 2196440 + 4003064 2 - 63781 - 75267 + 4 + 366883 + + + 4 + 2275 + 117172 @@ -15556,15 +15597,15 @@ permits - 177 + 129 id1 - 43 + 31 id2 - 177 + 129 @@ -15578,17 +15619,17 @@ 1 2 - 1 + 2 2 3 - 13 + 8 3 4 - 9 + 6 4 @@ -15598,25 +15639,20 @@ 5 6 - 5 + 2 6 - 7 - 4 - - - 7 8 2 8 9 - 1 + 2 - 10 + 9 11 2 @@ -15634,7 +15670,7 @@ 1 2 - 177 + 129 @@ -15644,15 +15680,15 @@ hasModifier - 249133355 + 270713939 id1 - 166385675 + 180180147 id2 - 13613 + 26891 @@ -15666,17 +15702,17 @@ 1 2 - 84119910 + 90230296 2 3 - 81783851 + 89365911 3 4 - 481914 + 583940 @@ -15690,54 +15726,74 @@ 12 - 31 - 32 - 1361 + 4 + 5 + 1920 + + + 14 + 15 + 1920 + + + 20 + 21 + 1920 + + + 21 + 22 + 1920 34 35 - 1361 + 1920 + + + 50 + 51 + 1920 109 110 - 1361 + 1920 - 267 - 268 - 1361 + 219 + 220 + 1920 - 500 - 501 - 1361 + 3864 + 3865 + 1920 - 8143 - 8144 - 1361 + 7193 + 7194 + 1920 - 18034 - 18035 - 1361 + 9195 + 9196 + 1920 - 18282 - 18283 - 1361 + 14706 + 14707 + 1920 - 20694 - 20695 - 1361 + 18810 + 18811 + 1920 - 116912 - 116913 - 1361 + 86695 + 86696 + 1920 @@ -15747,15 +15803,15 @@ imports - 368532 + 368550 id - 368532 + 368550 holder - 58075 + 58078 name @@ -15777,7 +15833,7 @@ 1 2 - 368532 + 368550 @@ -15793,7 +15849,7 @@ 1 2 - 368532 + 368550 @@ -15809,7 +15865,7 @@ 1 2 - 368532 + 368550 @@ -15825,7 +15881,7 @@ 1 2 - 28374 + 28375 2 @@ -15871,7 +15927,7 @@ 1 2 - 54591 + 54593 2 @@ -15892,7 +15948,7 @@ 1 2 - 55089 + 55091 2 @@ -15954,7 +16010,7 @@ 1 2 - 7300 + 7301 2 @@ -16078,27 +16134,27 @@ stmts - 2530730 + 2441387 id - 2530730 + 2441387 kind - 13613 + 9674 parent - 1783355 + 1353860 idx - 216453 + 15934 bodydecl - 703812 + 367630 @@ -16112,7 +16168,7 @@ 1 2 - 2530730 + 2441387 @@ -16128,7 +16184,7 @@ 1 2 - 2530730 + 2441387 @@ -16144,7 +16200,7 @@ 1 2 - 2530730 + 2441387 @@ -16160,7 +16216,7 @@ 1 2 - 2530730 + 2441387 @@ -16169,57 +16225,6 @@ kind id - - - 12 - - - 2 - 3 - 4084 - - - 4 - 5 - 1361 - - - 72 - 73 - 1361 - - - 96 - 97 - 1361 - - - 162 - 163 - 1361 - - - 373 - 374 - 1361 - - - 525 - 526 - 1361 - - - 621 - 622 - 1361 - - - - - - - kind - parent 12 @@ -16227,88 +16232,224 @@ 1 2 - 1361 - - - 2 - 3 - 2722 - - - 4 - 5 - 1361 - - - 53 - 54 - 1361 - - - 72 - 73 - 1361 - - - 98 - 99 - 1361 - - - 233 - 234 - 1361 - - - 373 - 374 - 1361 - - - 621 - 622 - 1361 - - - - - - - kind - idx - - - 12 - - - 1 - 2 - 5445 - - - 2 - 3 - 1361 - - - 5 - 6 - 1361 + 1707 6 7 - 1361 + 569 7 8 - 2722 + 569 - 158 - 159 - 1361 + 17 + 18 + 569 + + + 20 + 21 + 569 + + + 23 + 24 + 569 + + + 33 + 34 + 569 + + + 83 + 84 + 569 + + + 91 + 92 + 569 + + + 97 + 98 + 569 + + + 265 + 266 + 569 + + + 312 + 313 + 569 + + + 560 + 561 + 569 + + + 1243 + 1244 + 569 + + + 1530 + 1531 + 569 + + + + + + + kind + parent + + + 12 + + + 1 + 2 + 2276 + + + 7 + 8 + 569 + + + 12 + 13 + 569 + + + 17 + 18 + 569 + + + 21 + 22 + 569 + + + 33 + 34 + 569 + + + 71 + 72 + 569 + + + 81 + 82 + 569 + + + 91 + 92 + 569 + + + 246 + 247 + 569 + + + 265 + 266 + 569 + + + 271 + 272 + 569 + + + 716 + 717 + 569 + + + 1192 + 1193 + 569 + + + + + + + kind + idx + + + 12 + + + 1 + 2 + 2276 + + + 3 + 4 + 569 + + + 4 + 5 + 569 + + + 6 + 7 + 1138 + + + 7 + 8 + 569 + + + 8 + 9 + 1138 + + + 10 + 11 + 1138 + + + 13 + 14 + 569 + + + 17 + 18 + 569 + + + 21 + 22 + 569 + + + 26 + 27 + 569 @@ -16324,42 +16465,67 @@ 1 2 - 1361 + 2276 - 2 - 3 - 4084 + 7 + 8 + 1138 - 25 - 26 - 1361 + 17 + 18 + 569 - 71 - 72 - 1361 + 20 + 21 + 569 - 72 - 73 - 1361 + 21 + 22 + 569 + + + 53 + 54 + 569 + + + 54 + 55 + 569 + + + 91 + 92 + 569 119 120 - 1361 + 569 - 371 - 372 - 1361 + 179 + 180 + 569 - 517 - 518 - 1361 + 211 + 212 + 569 + + + 431 + 432 + 569 + + + 646 + 647 + 569 @@ -16375,17 +16541,22 @@ 1 2 - 1501557 + 989074 2 3 - 198755 + 191782 3 - 159 - 83041 + 6 + 107557 + + + 6 + 27 + 65445 @@ -16401,17 +16572,17 @@ 1 2 - 1587322 + 1081267 2 3 - 189226 + 194058 3 - 4 - 6806 + 6 + 78534 @@ -16427,17 +16598,22 @@ 1 2 - 1501557 + 989074 2 3 - 198755 + 191782 3 - 159 - 83041 + 6 + 107557 + + + 6 + 27 + 65445 @@ -16453,7 +16629,7 @@ 1 2 - 1783355 + 1353860 @@ -16466,25 +16642,70 @@ 12 - - 1 - 2 - 160638 - - - 2 - 3 - 34033 - 3 - 24 - 16336 + 5 + 1138 - 34 - 1213 - 5445 + 5 + 6 + 1138 + + + 6 + 7 + 2276 + + + 10 + 15 + 1138 + + + 15 + 16 + 1138 + + + 17 + 20 + 1138 + + + 24 + 32 + 1138 + + + 40 + 43 + 1138 + + + 55 + 72 + 1138 + + + 83 + 94 + 1138 + + + 115 + 160 + 1138 + + + 204 + 386 + 1138 + + + 939 + 1919 + 1138 @@ -16500,157 +16721,332 @@ 1 2 - 204200 - - - 2 - 9 - 12252 - - - - - - - idx - parent - - - 12 - - - 1 - 2 - 160638 + 2276 2 3 - 34033 + 3983 3 - 24 - 16336 + 5 + 1138 - 34 - 1213 - 5445 - - - - - - - idx - bodydecl - - - 12 - - - 1 - 2 - 160638 + 5 + 6 + 2276 - 2 - 3 - 34033 - - - 3 - 19 - 16336 - - - 29 - 518 - 5445 - - - - - - - bodydecl - id - - - 12 - - - 2 - 3 - 544535 - - - 3 - 4 - 58537 - - - 4 + 6 7 - 53092 + 2276 7 - 162 - 47646 - - - - - - - bodydecl - kind - - - 12 - - - 2 - 3 - 575846 - - - 3 - 4 - 81680 - - - 4 - 7 - 46285 - - - - - - - bodydecl - parent - - - 12 - - - 2 - 3 - 630300 - - - 3 - 8 - 57176 + 9 + 1138 9 - 47 - 16336 + 10 + 1138 + + + 12 + 14 + 1138 + + + 16 + 17 + 569 + + + + + + + idx + parent + + + 12 + + + 3 + 5 + 1138 + + + 5 + 6 + 1138 + + + 6 + 7 + 2276 + + + 10 + 15 + 1138 + + + 15 + 16 + 1138 + + + 17 + 20 + 1138 + + + 24 + 32 + 1138 + + + 40 + 43 + 1138 + + + 55 + 72 + 1138 + + + 83 + 94 + 1138 + + + 115 + 160 + 1138 + + + 204 + 386 + 1138 + + + 939 + 1919 + 1138 + + + + + + + idx + bodydecl + + + 12 + + + 3 + 5 + 1138 + + + 5 + 6 + 1138 + + + 6 + 7 + 2276 + + + 10 + 15 + 1138 + + + 15 + 16 + 1138 + + + 17 + 20 + 1138 + + + 24 + 32 + 1138 + + + 40 + 43 + 1138 + + + 54 + 56 + 1138 + + + 68 + 87 + 1138 + + + 104 + 138 + 1138 + + + 167 + 226 + 1138 + + + 337 + 647 + 1138 + + + + + + + bodydecl + id + + + 12 + + + 1 + 2 + 17641 + + + 2 + 3 + 157637 + + + 3 + 4 + 38128 + + + 4 + 5 + 33576 + + + 5 + 7 + 24470 + + + 7 + 10 + 29023 + + + 10 + 16 + 30730 + + + 16 + 34 + 27885 + + + 34 + 131 + 8536 + + + + + + + bodydecl + kind + + + 12 + + + 1 + 2 + 17641 + + + 2 + 3 + 193489 + + + 3 + 4 + 77965 + + + 4 + 5 + 29592 + + + 5 + 7 + 33007 + + + 7 + 11 + 15934 + + + + + + + bodydecl + parent + + + 12 + + + 1 + 2 + 17641 + + + 2 + 3 + 261780 + + + 4 + 5 + 33576 + + + 5 + 10 + 31299 + + + 10 + 88 + 23332 @@ -16666,22 +17062,37 @@ 1 2 - 544535 + 175279 2 3 - 92571 + 63737 3 + 4 + 27885 + + + 4 + 5 + 18210 + + + 5 7 - 54453 + 30161 7 - 159 - 12252 + 11 + 29592 + + + 11 + 28 + 22763 @@ -16691,11 +17102,11 @@ exprs - 7411134 + 7410663 id - 7411134 + 7410663 kind @@ -16703,11 +17114,11 @@ typeid - 115983 + 115976 parent - 5075960 + 5075638 idx @@ -16725,7 +17136,7 @@ 1 2 - 7411134 + 7410663 @@ -16741,7 +17152,7 @@ 1 2 - 7411134 + 7410663 @@ -16757,7 +17168,7 @@ 1 2 - 7411134 + 7410663 @@ -16773,7 +17184,7 @@ 1 2 - 7411134 + 7410663 @@ -17028,7 +17439,7 @@ 1 2 - 48884 + 48881 2 @@ -17038,7 +17449,7 @@ 3 6 - 10698 + 10697 6 @@ -17048,12 +17459,12 @@ 10 17 - 9272 + 9271 17 32 - 8723 + 8722 32 @@ -17079,32 +17490,32 @@ 1 2 - 57854 + 57851 2 3 - 18544 + 18543 3 4 - 8915 + 8914 4 5 - 14456 + 14455 5 6 - 9738 + 9737 6 32 - 6474 + 6473 @@ -17120,12 +17531,12 @@ 1 2 - 49048 + 49045 2 3 - 13414 + 13413 3 @@ -17135,7 +17546,7 @@ 5 8 - 8531 + 8530 8 @@ -17155,7 +17566,7 @@ 57 851 - 8723 + 8722 890 @@ -17176,22 +17587,22 @@ 1 2 - 58677 + 58673 2 3 - 18379 + 18378 3 4 - 14731 + 14730 4 5 - 19120 + 19119 5 @@ -17212,17 +17623,17 @@ 1 2 - 3222415 + 3222211 2 3 - 1522711 + 1522614 3 48 - 330833 + 330812 @@ -17238,17 +17649,17 @@ 1 2 - 3525515 + 3525291 2 3 - 1385549 + 1385461 3 8 - 164895 + 164884 @@ -17264,17 +17675,17 @@ 1 2 - 4085819 + 4085559 2 3 - 832130 + 832078 3 10 - 158009 + 157999 @@ -17290,17 +17701,17 @@ 1 2 - 3222415 + 3222211 2 3 - 1522711 + 1522614 3 48 - 330833 + 330812 @@ -17554,15 +17965,15 @@ exprsKotlinType - 7411134 + 7410663 id - 7411134 + 7410663 kttypeid - 12363 + 10930 @@ -17576,7 +17987,7 @@ 1 2 - 7411134 + 7410663 @@ -17592,17 +18003,17 @@ 1 2 - 9272 + 8197 2 3 - 2060 + 1821 - 7180 - 7181 - 1030 + 8123 + 8124 + 910 @@ -17612,15 +18023,15 @@ callableEnclosingExpr - 7299509 + 7299071 id - 7299509 + 7299071 callable_id - 19878 + 19877 @@ -17634,7 +18045,7 @@ 1 2 - 7299509 + 7299071 @@ -17705,7 +18116,7 @@ 73 177 - 1496 + 1495 179 @@ -17725,15 +18136,15 @@ statementEnclosingExpr - 7259150 + 7258714 id - 7259150 + 7258714 statement_id - 525810 + 525778 @@ -17747,7 +18158,7 @@ 1 2 - 7259150 + 7258714 @@ -17763,57 +18174,57 @@ 1 3 - 29127 + 29126 3 5 - 47080 + 47077 5 7 - 48477 + 48474 7 8 - 36044 + 36042 8 9 - 38147 + 38145 9 10 - 50450 + 50447 10 11 - 29214 + 29212 11 12 - 127057 + 127049 12 35 - 35324 + 35322 35 40 - 44626 + 44623 40 81 - 40224 + 40222 82 @@ -17828,11 +18239,11 @@ isParenthesized - 94658 + 94652 id - 94658 + 94652 parentheses @@ -17850,7 +18261,7 @@ 1 2 - 94658 + 94652 @@ -17881,37 +18292,37 @@ when_if - 83383 + 74334 id - 83383 + 74334 when_branch_else - 79912 + 76075 id - 79912 + 76075 callableBinding - 1832402 + 1832520 callerid - 1832402 + 1832520 callee - 263395 + 264311 @@ -17925,7 +18336,7 @@ 1 2 - 1832402 + 1832520 @@ -17941,32 +18352,32 @@ 1 2 - 162234 + 162993 2 3 - 33023 + 33137 3 4 - 16214 + 16271 4 7 - 22259 + 22271 7 20 - 19911 + 19902 20 46115 - 9751 + 9734 @@ -17976,15 +18387,15 @@ memberRefBinding - 23206 + 23208 id - 23206 + 23208 callable - 11196 + 11197 @@ -17998,7 +18409,7 @@ 1 2 - 23206 + 23208 @@ -18014,12 +18425,12 @@ 1 2 - 7861 + 7862 2 3 - 2074 + 2075 3 @@ -18039,15 +18450,15 @@ propertyRefGetBinding - 9639 + 8876 id - 9639 + 8876 getter - 5826 + 5366 @@ -18061,7 +18472,7 @@ 1 2 - 9639 + 8876 @@ -18077,17 +18488,17 @@ 1 2 - 2117 + 1951 2 3 - 3615 + 3328 3 6 - 94 + 86 @@ -18145,15 +18556,15 @@ propertyRefSetBinding - 2671 + 2600 id - 2671 + 2600 setter - 1335 + 1300 @@ -18167,7 +18578,7 @@ 1 2 - 2671 + 2600 @@ -18183,7 +18594,7 @@ 2 3 - 1335 + 1300 @@ -18193,15 +18604,15 @@ variableBinding - 2434432 + 2434277 expr - 2434432 + 2434277 variable - 572564 + 572528 @@ -18215,7 +18626,7 @@ 1 2 - 2434432 + 2434277 @@ -18231,37 +18642,37 @@ 1 2 - 205804 + 205791 2 3 - 120953 + 120945 3 4 - 85027 + 85022 4 5 - 45970 + 45967 5 7 - 40061 + 40059 7 14 - 43075 + 43072 14 464 - 31671 + 31669 @@ -18271,23 +18682,23 @@ localvars - 385297 + 385272 id - 385297 + 385272 nodeName - 140004 + 139995 typeid - 49513 + 49510 parentid - 385297 + 385272 @@ -18301,7 +18712,7 @@ 1 2 - 385297 + 385272 @@ -18317,7 +18728,7 @@ 1 2 - 385297 + 385272 @@ -18333,7 +18744,7 @@ 1 2 - 385297 + 385272 @@ -18349,22 +18760,22 @@ 1 2 - 83661 + 83655 2 3 - 26179 + 26178 3 5 - 10244 + 10243 5 9 - 11951 + 11950 9 @@ -18385,7 +18796,7 @@ 1 2 - 124638 + 124630 2 @@ -18411,22 +18822,22 @@ 1 2 - 83661 + 83655 2 3 - 26179 + 26178 3 5 - 10244 + 10243 5 9 - 11951 + 11950 9 @@ -18447,7 +18858,7 @@ 1 2 - 16504 + 16503 2 @@ -18462,7 +18873,7 @@ 5 6 - 5122 + 5121 6 @@ -18503,7 +18914,7 @@ 1 2 - 23334 + 23332 2 @@ -18513,7 +18924,7 @@ 3 4 - 6260 + 6259 4 @@ -18544,7 +18955,7 @@ 1 2 - 16504 + 16503 2 @@ -18559,7 +18970,7 @@ 5 6 - 5122 + 5121 6 @@ -18600,7 +19011,7 @@ 1 2 - 385297 + 385272 @@ -18616,7 +19027,7 @@ 1 2 - 385297 + 385272 @@ -18632,7 +19043,7 @@ 1 2 - 385297 + 385272 @@ -18642,15 +19053,15 @@ localvarsKotlinType - 227691 + 240107 id - 227691 + 240107 kttypeid - 154 + 1920 @@ -18664,7 +19075,7 @@ 1 2 - 227691 + 240107 @@ -18678,9 +19089,9 @@ 12 - 1470 - 1471 - 154 + 125 + 126 + 1920 @@ -18690,19 +19101,19 @@ namestrings - 4022677 + 4022436 name - 23386 + 23384 value - 22167 + 22166 parent - 4022677 + 4022436 @@ -18716,7 +19127,7 @@ 1 2 - 23386 + 23384 @@ -18732,7 +19143,7 @@ 1 2 - 9605 + 9604 2 @@ -18783,7 +19194,7 @@ 1 2 - 21560 + 21559 2 @@ -18804,7 +19215,7 @@ 1 2 - 9119 + 9118 2 @@ -18855,7 +19266,7 @@ 1 2 - 4022677 + 4022436 @@ -18871,7 +19282,7 @@ 1 2 - 4022677 + 4022436 @@ -18881,15 +19292,15 @@ modules - 7964 + 7965 id - 7964 + 7965 name - 7964 + 7965 @@ -18903,7 +19314,7 @@ 1 2 - 7964 + 7965 @@ -18919,7 +19330,7 @@ 1 2 - 7964 + 7965 @@ -18940,11 +19351,11 @@ cumodule - 247568 + 247580 fileId - 247568 + 247580 moduleId @@ -18962,7 +19373,7 @@ 1 2 - 247568 + 247580 @@ -19013,7 +19424,7 @@ directives - 50277 + 50279 id @@ -19021,7 +19432,7 @@ directive - 50277 + 50279 @@ -19081,7 +19492,7 @@ 1 2 - 50277 + 50279 @@ -19171,15 +19582,15 @@ exports - 35011 + 35013 id - 35011 + 35013 target - 35011 + 35013 @@ -19193,7 +19604,7 @@ 1 2 - 35011 + 35013 @@ -19209,7 +19620,7 @@ 1 2 - 35011 + 35013 @@ -19219,15 +19630,15 @@ exportsTo - 28706 + 28707 id - 12278 + 12279 target - 7466 + 7467 @@ -19241,7 +19652,7 @@ 1 2 - 7300 + 7301 2 @@ -19423,15 +19834,15 @@ uses - 10785 + 10786 id - 10785 + 10786 serviceInterface - 10785 + 10786 @@ -19445,7 +19856,7 @@ 1 2 - 10785 + 10786 @@ -19461,7 +19872,7 @@ 1 2 - 10785 + 10786 @@ -19519,7 +19930,7 @@ providesWith - 5309 + 5310 id @@ -19527,7 +19938,7 @@ serviceImpl - 5309 + 5310 @@ -19572,7 +19983,7 @@ 1 2 - 5309 + 5310 @@ -19582,48 +19993,48 @@ javadoc - 985153 + 985091 id - 985153 + 985091 isNormalComment - 649939 + 649898 commentid - 649939 + 649898 isEolComment - 610101 + 610062 commentid - 610101 + 610062 hasJavadoc - 435379 + 435352 documentableid - 368223 + 368199 javadocid - 435379 + 435352 @@ -19637,12 +20048,12 @@ 1 2 - 320416 + 320396 2 3 - 44960 + 44957 3 @@ -19663,7 +20074,7 @@ 1 2 - 435379 + 435352 @@ -19673,19 +20084,19 @@ javadocTag - 335830 + 335808 id - 335830 + 335808 name - 578 + 577 parentid - 114117 + 114110 idx @@ -19703,7 +20114,7 @@ 1 2 - 335830 + 335808 @@ -19719,7 +20130,7 @@ 1 2 - 335830 + 335808 @@ -19735,7 +20146,7 @@ 1 2 - 335830 + 335808 @@ -19884,37 +20295,37 @@ 1 2 - 33194 + 33192 2 3 - 24937 + 24935 3 4 - 18661 + 18660 4 5 - 13129 + 13128 5 6 - 9991 + 9990 6 7 - 7679 + 7678 7 11 - 6523 + 6522 @@ -19930,22 +20341,22 @@ 1 2 - 39966 + 39963 2 3 - 38644 + 38642 3 4 - 21139 + 21137 4 5 - 12221 + 12220 5 @@ -19966,37 +20377,37 @@ 1 2 - 33194 + 33192 2 3 - 24937 + 24935 3 4 - 18661 + 18660 4 5 - 13129 + 13128 5 6 - 9991 + 9990 6 7 - 7679 + 7678 7 11 - 6523 + 6522 @@ -20184,23 +20595,23 @@ javadocText - 2503007 + 2502848 id - 2503007 + 2502848 text - 1370450 + 1370363 parentid - 1169550 + 1169475 idx - 42115 + 42112 @@ -20214,7 +20625,7 @@ 1 2 - 2503007 + 2502848 @@ -20230,7 +20641,7 @@ 1 2 - 2503007 + 2502848 @@ -20246,7 +20657,7 @@ 1 2 - 2503007 + 2502848 @@ -20262,17 +20673,17 @@ 1 2 - 1139386 + 1139314 2 3 - 149679 + 149670 3 147 - 81384 + 81379 @@ -20288,17 +20699,17 @@ 1 2 - 1140524 + 1140452 2 3 - 149110 + 149101 3 88 - 80815 + 80810 @@ -20314,12 +20725,12 @@ 1 2 - 1346547 + 1346462 2 32 - 23903 + 23901 @@ -20335,22 +20746,22 @@ 1 2 - 870190 + 870135 2 3 - 159354 + 159344 3 12 - 83092 + 83086 12 75 - 56912 + 56908 @@ -20366,22 +20777,22 @@ 1 2 - 870190 + 870135 2 3 - 159354 + 159344 3 12 - 84230 + 84225 12 67 - 55774 + 55770 @@ -20397,22 +20808,22 @@ 1 2 - 870190 + 870135 2 3 - 159354 + 159344 3 12 - 83092 + 83086 12 75 - 56912 + 56908 @@ -20428,7 +20839,7 @@ 2 3 - 21057 + 21056 3 @@ -20484,7 +20895,7 @@ 2 3 - 20488 + 20487 3 @@ -20535,7 +20946,7 @@ 2 3 - 21057 + 21056 3 @@ -20580,15 +20991,15 @@ xmlEncoding - 800467 + 1129463 id - 800467 + 1129463 encoding - 1361 + 1920 @@ -20602,7 +21013,7 @@ 1 2 - 800467 + 1129463 @@ -20618,7 +21029,7 @@ 588 589 - 1361 + 1920 @@ -20976,27 +21387,27 @@ xmlElements - 106507143 + 150282022 id - 106507143 + 150282022 name - 337612 + 476372 parentid - 2747183 + 3876287 idx - 1207508 + 1703799 fileid - 800467 + 1129463 @@ -21010,7 +21421,7 @@ 1 2 - 106507143 + 150282022 @@ -21026,7 +21437,7 @@ 1 2 - 106507143 + 150282022 @@ -21042,7 +21453,7 @@ 1 2 - 106507143 + 150282022 @@ -21058,7 +21469,7 @@ 1 2 - 106507143 + 150282022 @@ -21074,57 +21485,57 @@ 1 2 - 106184 + 149826 2 3 - 40840 + 57625 3 4 - 16336 + 23050 4 6 - 29949 + 42258 6 8 - 24504 + 34575 8 9 - 12252 + 17287 9 10 - 23142 + 32654 10 18 - 28588 + 40337 18 48 - 25865 + 36496 52 250 - 25865 + 36496 342 73380 - 4084 + 5762 @@ -21140,52 +21551,52 @@ 1 2 - 123881 + 174797 2 3 - 46285 + 65309 3 4 - 17697 + 24971 4 5 - 14974 + 21129 5 6 - 19058 + 26891 6 8 - 28588 + 40337 8 10 - 28588 + 40337 10 21 - 27226 + 38417 22 128 - 25865 + 36496 130 229 - 5445 + 7683 @@ -21201,37 +21612,37 @@ 1 2 - 186503 + 263157 2 3 - 49008 + 69150 3 4 - 24504 + 34575 4 6 - 20420 + 28812 6 9 - 20420 + 28812 9 38 - 25865 + 36496 45 888 - 10890 + 15366 @@ -21247,42 +21658,42 @@ 1 2 - 183780 + 259315 2 3 - 36756 + 51863 3 4 - 17697 + 24971 4 5 - 13613 + 19208 5 7 - 29949 + 42258 7 16 - 25865 + 36496 17 114 - 27226 + 38417 118 131 - 2722 + 3841 @@ -21298,32 +21709,32 @@ 1 2 - 1671725 + 2358811 2 3 - 428822 + 605069 3 4 - 178335 + 251632 4 8 - 213730 + 301574 8 777 - 224621 + 316941 777 888 - 29949 + 42258 @@ -21339,17 +21750,17 @@ 1 2 - 2267992 + 3200146 2 3 - 291326 + 411063 3 17 - 187864 + 265078 @@ -21365,32 +21776,32 @@ 1 2 - 1671725 + 2358811 2 3 - 428822 + 605069 3 4 - 178335 + 251632 4 8 - 213730 + 301574 8 777 - 224621 + 316941 777 888 - 29949 + 42258 @@ -21406,7 +21817,7 @@ 1 2 - 2747183 + 3876287 @@ -21422,67 +21833,67 @@ 2 8 - 102100 + 144064 9 76 - 96655 + 136380 76 82 - 91209 + 128697 82 89 - 87125 + 122934 89 92 - 78957 + 111409 92 95 - 95293 + 134459 95 97 - 106184 + 149826 97 98 - 149747 + 211294 98 99 - 92571 + 130618 99 104 - 110268 + 155589 104 106 - 92571 + 130618 106 159 - 91209 + 128697 162 2019 - 13613 + 19208 @@ -21498,22 +21909,22 @@ 1 2 - 978803 + 1381095 2 5 - 89848 + 126776 5 9 - 103461 + 145985 9 150 - 35394 + 49942 @@ -21529,67 +21940,67 @@ 2 8 - 102100 + 144064 9 76 - 96655 + 136380 76 82 - 91209 + 128697 82 89 - 87125 + 122934 89 92 - 78957 + 111409 92 95 - 95293 + 134459 95 97 - 106184 + 149826 97 98 - 149747 + 211294 98 99 - 92571 + 130618 99 104 - 110268 + 155589 104 106 - 92571 + 130618 106 159 - 91209 + 128697 162 2019 - 13613 + 19208 @@ -21605,67 +22016,67 @@ 2 8 - 102100 + 144064 9 76 - 96655 + 136380 76 82 - 91209 + 128697 82 89 - 87125 + 122934 89 92 - 78957 + 111409 92 95 - 95293 + 134459 95 97 - 106184 + 149826 97 98 - 149747 + 211294 98 99 - 92571 + 130618 99 104 - 110268 + 155589 104 106 - 92571 + 130618 106 139 - 91209 + 128697 141 589 - 13613 + 19208 @@ -21681,57 +22092,57 @@ 1 2 - 58537 + 82596 2 3 - 134772 + 190164 3 4 - 176974 + 249711 4 5 - 74873 + 105647 5 7 - 59898 + 84517 7 10 - 66705 + 94121 10 31 - 62621 + 88359 35 694 - 61260 + 86438 738 776 - 20420 + 28812 777 779 - 65344 + 92201 788 889 - 19058 + 26891 @@ -21747,37 +22158,37 @@ 1 2 - 58537 + 82596 2 3 - 398872 + 562810 3 4 - 138856 + 195927 4 5 - 65344 + 92201 5 6 - 61260 + 86438 6 9 - 65344 + 92201 9 69 - 12252 + 17287 @@ -21793,27 +22204,27 @@ 1 2 - 58537 + 82596 2 3 - 567678 + 800997 3 4 - 57176 + 80675 4 6 - 58537 + 82596 6 165 - 58537 + 82596 @@ -21829,42 +22240,42 @@ 1 2 - 202839 + 286207 2 3 - 219175 + 309257 3 4 - 88487 + 124855 4 7 - 66705 + 94121 7 17 - 66705 + 94121 18 763 - 61260 + 86438 764 777 - 65344 + 92201 777 888 - 29949 + 42258 @@ -21874,31 +22285,31 @@ xmlAttrs - 129551904 + 182798274 id - 129551904 + 182798274 elementid - 105600491 + 149002731 name - 511863 + 722241 value - 8184375 + 11548187 idx - 31310 + 44179 fileid - 799106 + 1127542 @@ -21912,7 +22323,7 @@ 1 2 - 129551904 + 182798274 @@ -21928,7 +22339,7 @@ 1 2 - 129551904 + 182798274 @@ -21944,7 +22355,7 @@ 1 2 - 129551904 + 182798274 @@ -21960,7 +22371,7 @@ 1 2 - 129551904 + 182798274 @@ -21976,7 +22387,7 @@ 1 2 - 129551904 + 182798274 @@ -21992,17 +22403,17 @@ 1 2 - 96634707 + 136351973 2 6 - 7940695 + 11204353 6 24 - 1025088 + 1446404 @@ -22018,17 +22429,17 @@ 1 2 - 96634707 + 136351973 2 6 - 7954308 + 11223562 6 23 - 1011475 + 1427196 @@ -22044,17 +22455,17 @@ 1 2 - 96687799 + 136426886 2 6 - 7993787 + 11279267 6 21 - 918904 + 1296577 @@ -22070,17 +22481,17 @@ 1 2 - 96634707 + 136351973 2 6 - 7940695 + 11204353 6 24 - 1025088 + 1446404 @@ -22096,7 +22507,7 @@ 1 2 - 105600491 + 149002731 @@ -22112,62 +22523,62 @@ 1 2 - 106184 + 149826 2 3 - 55814 + 78755 3 4 - 31310 + 44179 4 5 - 17697 + 24971 5 6 - 29949 + 42258 6 8 - 36756 + 51863 8 11 - 42201 + 59546 11 22 - 39478 + 55704 23 38 - 40840 + 57625 38 79 - 40840 + 57625 81 168 - 39478 + 55704 168 74700 - 31310 + 44179 @@ -22183,62 +22594,62 @@ 1 2 - 106184 + 149826 2 3 - 55814 + 78755 3 4 - 31310 + 44179 4 5 - 17697 + 24971 5 6 - 29949 + 42258 6 8 - 36756 + 51863 8 11 - 46285 + 65309 11 25 - 43562 + 61467 25 39 - 42201 + 59546 43 91 - 40840 + 57625 91 227 - 39478 + 55704 227 74700 - 21781 + 30733 @@ -22254,42 +22665,42 @@ 1 2 - 215091 + 303495 2 3 - 80319 + 113330 3 4 - 34033 + 48021 4 5 - 36756 + 51863 5 9 - 42201 + 59546 9 21 - 39478 + 55704 22 64 - 42201 + 59546 68 2100 - 21781 + 30733 @@ -22305,37 +22716,37 @@ 1 2 - 201478 + 284286 2 3 - 95293 + 134459 3 4 - 49008 + 69150 4 5 - 54453 + 76834 5 7 - 32672 + 46100 7 10 - 40840 + 57625 10 21 - 38117 + 53783 @@ -22351,52 +22762,52 @@ 1 2 - 178335 + 251632 2 3 - 53092 + 74913 3 4 - 27226 + 38417 4 5 - 24504 + 34575 5 6 - 38117 + 53783 6 9 - 42201 + 59546 9 17 - 44924 + 63388 17 34 - 39478 + 55704 36 91 - 39478 + 55704 91 223 - 24504 + 34575 @@ -22412,37 +22823,37 @@ 1 2 - 4470639 + 6308091 2 3 - 1210231 + 1707641 3 5 - 627577 + 885514 5 31 - 616686 + 870147 31 91 - 643913 + 908564 91 1111 - 613964 + 866306 3397 3398 - 1361 + 1920 @@ -22458,32 +22869,32 @@ 1 2 - 4560488 + 6434868 2 3 - 1153054 + 1626965 3 5 - 635745 + 897039 5 33 - 645275 + 910485 33 93 - 631661 + 891277 93 3398 - 558149 + 787551 @@ -22499,17 +22910,17 @@ 1 2 - 7427470 + 10480191 2 4 - 658888 + 929694 4 53 - 98016 + 138301 @@ -22525,17 +22936,17 @@ 1 2 - 6778110 + 9563942 2 3 - 989694 + 1396462 3 20 - 416569 + 587781 @@ -22551,32 +22962,32 @@ 1 2 - 5268385 + 7433713 2 3 - 887593 + 1252398 3 10 - 635745 + 897039 10 83 - 622132 + 877831 83 99 - 624854 + 881672 99 182 - 145663 + 205531 @@ -22592,62 +23003,62 @@ 1 6 - 2722 + 3841 12 14 - 2722 + 3841 17 26 - 2722 + 3841 39 56 - 2722 + 3841 83 110 - 2722 + 3841 153 232 - 2722 + 3841 316 400 - 2722 + 3841 468 545 - 2722 + 3841 626 754 - 2722 + 3841 951 1491 - 2722 + 3841 4718 6587 - 2722 + 3841 77571 77572 - 1361 + 1920 @@ -22663,62 +23074,62 @@ 1 6 - 2722 + 3841 12 14 - 2722 + 3841 17 26 - 2722 + 3841 39 56 - 2722 + 3841 83 110 - 2722 + 3841 153 232 - 2722 + 3841 316 400 - 2722 + 3841 468 545 - 2722 + 3841 626 754 - 2722 + 3841 951 1491 - 2722 + 3841 4718 6587 - 2722 + 3841 77571 77572 - 1361 + 1920 @@ -22734,62 +23145,62 @@ 1 4 - 2722 + 3841 7 10 - 2722 + 3841 11 17 - 2722 + 3841 18 23 - 2722 + 3841 26 38 - 2722 + 3841 39 49 - 2722 + 3841 57 67 - 2722 + 3841 72 79 - 2722 + 3841 95 101 - 2722 + 3841 105 106 - 2722 + 3841 106 132 - 2722 + 3841 140 141 - 1361 + 1920 @@ -22805,62 +23216,62 @@ 1 5 - 2722 + 3841 7 10 - 2722 + 3841 11 18 - 2722 + 3841 22 32 - 2722 + 3841 46 63 - 2722 + 3841 85 119 - 2722 + 3841 142 185 - 2722 + 3841 212 228 - 2722 + 3841 253 275 - 2722 + 3841 307 423 - 2722 + 3841 580 1324 - 2722 + 3841 3579 3580 - 1361 + 1920 @@ -22876,62 +23287,62 @@ 1 6 - 2722 + 3841 7 8 - 2722 + 3841 10 19 - 2722 + 3841 23 36 - 2722 + 3841 45 59 - 2722 + 3841 73 97 - 2722 + 3841 115 131 - 2722 + 3841 140 148 - 2722 + 3841 168 181 - 2722 + 3841 248 363 - 2722 + 3841 473 530 - 2722 + 3841 587 588 - 1361 + 1920 @@ -22947,72 +23358,72 @@ 1 3 - 59898 + 84517 3 5 - 61260 + 86438 5 6 - 36756 + 51863 6 7 - 61260 + 86438 7 8 - 51730 + 72992 8 10 - 58537 + 82596 10 15 - 65344 + 92201 15 27 - 61260 + 86438 27 41 - 61260 + 86438 41 65 - 61260 + 86438 65 157 - 65344 + 92201 162 817 - 61260 + 86438 818 832 - 66705 + 94121 832 1187 - 27226 + 38417 @@ -23028,52 +23439,52 @@ 1 2 - 91209 + 128697 2 3 - 187864 + 265078 3 4 - 112991 + 159431 4 5 - 77596 + 109488 5 8 - 72151 + 101805 8 14 - 61260 + 86438 14 295 - 61260 + 86438 330 775 - 50369 + 71071 776 778 - 65344 + 92201 787 888 - 19058 + 26891 @@ -23089,62 +23500,62 @@ 1 2 - 50369 + 71071 2 3 - 65344 + 92201 3 4 - 50369 + 71071 4 5 - 66705 + 94121 5 6 - 121159 + 170956 6 7 - 114352 + 161351 7 8 - 50369 + 71071 8 12 - 61260 + 86438 12 18 - 69428 + 97963 18 24 - 68066 + 96042 24 37 - 62621 + 88359 37 55 - 19058 + 26891 @@ -23160,67 +23571,67 @@ 1 3 - 69428 + 97963 3 4 - 39478 + 55704 4 5 - 73512 + 103726 5 6 - 88487 + 124855 6 8 - 62621 + 88359 8 12 - 70789 + 99884 12 19 - 61260 + 86438 19 27 - 69428 + 97963 27 41 - 61260 + 86438 42 170 - 61260 + 86438 205 780 - 57176 + 80675 781 783 - 65344 + 92201 791 893 - 19058 + 26891 @@ -23236,47 +23647,47 @@ 1 2 - 78957 + 111409 2 3 - 76235 + 107567 3 4 - 151108 + 213215 4 5 - 155192 + 218977 5 6 - 92571 + 130618 6 10 - 68066 + 96042 10 12 - 46285 + 65309 12 15 - 69428 + 97963 15 24 - 61260 + 86438 @@ -23286,23 +23697,23 @@ xmlNs - 1283743 + 1811367 id - 8168 + 11525 prefixName - 9529 + 13445 URI - 8168 + 11525 fileid - 747375 + 1054550 @@ -23316,12 +23727,12 @@ 1 2 - 6806 + 9604 2 3 - 1361 + 1920 @@ -23337,7 +23748,7 @@ 1 2 - 8168 + 11525 @@ -23353,32 +23764,32 @@ 2 3 - 1361 + 1920 20 21 - 1361 + 1920 88 89 - 1361 + 1920 167 168 - 1361 + 1920 213 214 - 1361 + 1920 453 454 - 1361 + 1920 @@ -23394,7 +23805,7 @@ 1 2 - 9529 + 13445 @@ -23410,7 +23821,7 @@ 1 2 - 9529 + 13445 @@ -23426,37 +23837,37 @@ 1 2 - 1361 + 1920 2 3 - 1361 + 1920 20 21 - 1361 + 1920 88 89 - 1361 + 1920 166 167 - 1361 + 1920 213 214 - 1361 + 1920 453 454 - 1361 + 1920 @@ -23472,7 +23883,7 @@ 1 2 - 8168 + 11525 @@ -23488,12 +23899,12 @@ 1 2 - 6806 + 9604 2 3 - 1361 + 1920 @@ -23509,32 +23920,32 @@ 2 3 - 1361 + 1920 20 21 - 1361 + 1920 88 89 - 1361 + 1920 167 168 - 1361 + 1920 213 214 - 1361 + 1920 453 454 - 1361 + 1920 @@ -23550,17 +23961,17 @@ 1 2 - 333528 + 470609 2 3 - 291326 + 411063 3 4 - 122520 + 172877 @@ -23576,17 +23987,17 @@ 1 2 - 333528 + 470609 2 3 - 291326 + 411063 3 4 - 122520 + 172877 @@ -23602,17 +24013,17 @@ 1 2 - 333528 + 470609 2 3 - 291326 + 411063 3 4 - 122520 + 172877 @@ -23622,19 +24033,19 @@ xmlHasNs - 25896767 + 36540446 elementId - 25896767 + 36540446 nsId - 8168 + 11525 fileid - 743291 + 1048787 @@ -23648,7 +24059,7 @@ 1 2 - 25896767 + 36540446 @@ -23664,7 +24075,7 @@ 1 2 - 25896767 + 36540446 @@ -23680,32 +24091,32 @@ 13 14 - 1361 + 1920 84 85 - 1361 + 1920 2426 2427 - 1361 + 1920 2733 2734 - 1361 + 1920 3704 3705 - 1361 + 1920 10063 10064 - 1361 + 1920 @@ -23721,32 +24132,32 @@ 2 3 - 1361 + 1920 20 21 - 1361 + 1920 86 87 - 1361 + 1920 164 165 - 1361 + 1920 209 210 - 1361 + 1920 453 454 - 1361 + 1920 @@ -23762,77 +24173,77 @@ 1 3 - 44924 + 63388 3 5 - 62621 + 88359 5 6 - 34033 + 48021 6 7 - 65344 + 92201 7 8 - 49008 + 69150 8 10 - 61260 + 86438 10 15 - 65344 + 92201 15 25 - 55814 + 78755 25 36 - 57176 + 80675 36 49 - 58537 + 82596 49 54 - 14974 + 21129 54 55 - 58537 + 82596 55 81 - 57176 + 80675 81 298 - 55814 + 78755 298 833 - 2722 + 3841 @@ -23848,17 +24259,17 @@ 1 2 - 334889 + 472530 2 3 - 288604 + 407221 3 4 - 119797 + 169035 @@ -23868,23 +24279,23 @@ xmlComments - 107198704 + 151257816 id - 107198704 + 151257816 text - 1692145 + 2387624 parentid - 839946 + 1185168 fileid - 785493 + 1108333 @@ -23898,7 +24309,7 @@ 1 2 - 107198704 + 151257816 @@ -23914,7 +24325,7 @@ 1 2 - 107198704 + 151257816 @@ -23930,7 +24341,7 @@ 1 2 - 107198704 + 151257816 @@ -23946,67 +24357,67 @@ 1 2 - 232789 + 328466 2 7 - 138856 + 195927 7 32 - 140218 + 197848 32 61 - 127965 + 180560 61 76 - 127965 + 180560 76 84 - 136133 + 192085 84 90 - 125243 + 176718 90 94 - 111629 + 157510 94 95 - 59898 + 84517 95 96 - 100739 + 142143 96 98 - 140218 + 197848 98 100 - 126604 + 178639 100 460 - 123881 + 174797 @@ -24022,67 +24433,67 @@ 1 2 - 235511 + 332308 2 6 - 134772 + 190164 6 32 - 142940 + 201689 32 61 - 129327 + 182481 61 75 - 132049 + 186323 75 84 - 148386 + 209373 84 90 - 122520 + 172877 90 94 - 119797 + 169035 94 95 - 66705 + 94121 95 96 - 103461 + 145985 96 98 - 142940 + 201689 98 100 - 134772 + 190164 100 460 - 78957 + 111409 @@ -24098,67 +24509,67 @@ 1 2 - 246402 + 347674 2 7 - 133411 + 188243 7 32 - 133411 + 188243 32 61 - 129327 + 182481 61 75 - 132049 + 186323 75 84 - 148386 + 209373 84 90 - 122520 + 172877 90 94 - 119797 + 169035 94 95 - 66705 + 94121 95 96 - 103461 + 145985 96 98 - 142940 + 201689 98 100 - 134772 + 190164 100 460 - 78957 + 111409 @@ -24174,22 +24585,22 @@ 1 2 - 668417 + 943140 2 724 - 63982 + 90280 726 830 - 77596 + 109488 831 941 - 29949 + 42258 @@ -24205,27 +24616,27 @@ 1 2 - 668417 + 943140 2 697 - 63982 + 90280 697 795 - 34033 + 48021 795 827 - 63982 + 90280 838 899 - 9529 + 13445 @@ -24241,7 +24652,7 @@ 1 2 - 839946 + 1185168 @@ -24257,27 +24668,27 @@ 1 2 - 600350 + 847097 2 549 - 59898 + 84517 579 829 - 40840 + 57625 829 832 - 65344 + 92201 834 941 - 19058 + 26891 @@ -24293,27 +24704,27 @@ 1 2 - 600350 + 847097 2 536 - 59898 + 84517 560 795 - 51730 + 72992 795 812 - 59898 + 84517 819 899 - 13613 + 19208 @@ -24329,12 +24740,12 @@ 1 2 - 746014 + 1052629 2 6 - 39478 + 55704 @@ -24344,31 +24755,31 @@ xmlChars - 101302741 + 142938589 id - 101302741 + 142938589 text - 77797848 + 109773086 parentid - 101302741 + 142938589 idx - 1361 + 1920 isCDATA - 2722 + 3841 fileid - 176974 + 249711 @@ -24382,7 +24793,7 @@ 1 2 - 101302741 + 142938589 @@ -24398,7 +24809,7 @@ 1 2 - 101302741 + 142938589 @@ -24414,7 +24825,7 @@ 1 2 - 101302741 + 142938589 @@ -24430,7 +24841,7 @@ 1 2 - 101302741 + 142938589 @@ -24446,7 +24857,7 @@ 1 2 - 101302741 + 142938589 @@ -24462,17 +24873,17 @@ 1 2 - 66568156 + 93927944 2 3 - 6991841 + 9865517 3 128 - 4237850 + 5979625 @@ -24488,17 +24899,17 @@ 1 2 - 66568156 + 93927944 2 3 - 6991841 + 9865517 3 128 - 4237850 + 5979625 @@ -24514,7 +24925,7 @@ 1 2 - 77797848 + 109773086 @@ -24530,7 +24941,7 @@ 1 2 - 77797848 + 109773086 @@ -24546,12 +24957,12 @@ 1 2 - 74436700 + 105030493 2 76 - 3361148 + 4742593 @@ -24567,7 +24978,7 @@ 1 2 - 101302741 + 142938589 @@ -24583,7 +24994,7 @@ 1 2 - 101302741 + 142938589 @@ -24599,7 +25010,7 @@ 1 2 - 101302741 + 142938589 @@ -24615,7 +25026,7 @@ 1 2 - 101302741 + 142938589 @@ -24631,7 +25042,7 @@ 1 2 - 101302741 + 142938589 @@ -24647,7 +25058,7 @@ 74414 74415 - 1361 + 1920 @@ -24663,7 +25074,7 @@ 57148 57149 - 1361 + 1920 @@ -24679,7 +25090,7 @@ 74414 74415 - 1361 + 1920 @@ -24695,7 +25106,7 @@ 2 3 - 1361 + 1920 @@ -24711,7 +25122,7 @@ 130 131 - 1361 + 1920 @@ -24727,12 +25138,12 @@ 518 519 - 1361 + 1920 73896 73897 - 1361 + 1920 @@ -24748,12 +25159,12 @@ 492 493 - 1361 + 1920 56656 56657 - 1361 + 1920 @@ -24769,12 +25180,12 @@ 518 519 - 1361 + 1920 73896 73897 - 1361 + 1920 @@ -24790,7 +25201,7 @@ 1 2 - 2722 + 3841 @@ -24806,12 +25217,12 @@ 98 99 - 1361 + 1920 130 131 - 1361 + 1920 @@ -24827,57 +25238,57 @@ 1 2 - 14974 + 21129 2 23 - 13613 + 19208 24 243 - 13613 + 19208 294 566 - 13613 + 19208 610 686 - 13613 + 19208 691 764 - 13613 + 19208 765 775 - 13613 + 19208 775 776 - 4084 + 5762 776 777 - 49008 + 69150 777 803 - 13613 + 19208 807 888 - 13613 + 19208 @@ -24893,67 +25304,67 @@ 1 2 - 14974 + 21129 2 21 - 13613 + 19208 22 188 - 13613 + 19208 208 492 - 13613 + 19208 525 589 - 13613 + 19208 590 638 - 13613 + 19208 639 651 - 13613 + 19208 652 656 - 12252 + 17287 656 659 - 16336 + 23050 659 663 - 14974 + 21129 663 667 - 13613 + 19208 667 701 - 13613 + 19208 702 744 - 9529 + 13445 @@ -24969,57 +25380,57 @@ 1 2 - 14974 + 21129 2 23 - 13613 + 19208 24 243 - 13613 + 19208 294 566 - 13613 + 19208 610 686 - 13613 + 19208 691 764 - 13613 + 19208 765 775 - 13613 + 19208 775 776 - 4084 + 5762 776 777 - 49008 + 69150 777 803 - 13613 + 19208 807 888 - 13613 + 19208 @@ -25035,7 +25446,7 @@ 1 2 - 176974 + 249711 @@ -25051,12 +25462,12 @@ 1 2 - 43562 + 61467 2 3 - 133411 + 188243 @@ -25066,15 +25477,15 @@ xmllocations - 446644705 + 630217533 xmlElement - 445369130 + 628417691 location - 418533038 + 590551854 @@ -25088,12 +25499,12 @@ 1 2 - 445360961 + 628406166 2 454 - 8168 + 11525 @@ -25109,12 +25520,12 @@ 1 2 - 409045860 + 577165407 2 25 - 9487177 + 13386446 @@ -25355,19 +25766,19 @@ ktComments - 133411 + 188243 id - 133411 + 188243 kind - 4084 + 5762 text - 96655 + 136380 @@ -25381,7 +25792,7 @@ 1 2 - 133411 + 188243 @@ -25397,7 +25808,7 @@ 1 2 - 133411 + 188243 @@ -25413,17 +25824,17 @@ 16 17 - 1361 + 1920 22 23 - 1361 + 1920 60 61 - 1361 + 1920 @@ -25439,17 +25850,17 @@ 1 2 - 1361 + 1920 16 17 - 1361 + 1920 54 55 - 1361 + 1920 @@ -25465,12 +25876,12 @@ 1 2 - 92571 + 130618 4 23 - 4084 + 5762 @@ -25486,7 +25897,7 @@ 1 2 - 96655 + 136380 @@ -25496,19 +25907,19 @@ ktCommentSections - 59896 + 52611 id - 59896 + 52611 comment - 54765 + 48161 content - 50913 + 44765 @@ -25522,7 +25933,7 @@ 1 2 - 59896 + 52611 @@ -25538,7 +25949,7 @@ 1 2 - 59896 + 52611 @@ -25554,12 +25965,12 @@ 1 2 - 52697 + 46367 2 18 - 2068 + 1793 @@ -25575,12 +25986,12 @@ 1 2 - 52697 + 46367 2 18 - 2068 + 1793 @@ -25596,17 +26007,17 @@ 1 2 - 45040 + 39616 2 3 - 4909 + 4313 3 63 - 963 + 835 @@ -25622,17 +26033,17 @@ 1 2 - 45151 + 39712 2 3 - 4815 + 4231 3 56 - 947 + 821 @@ -25642,15 +26053,15 @@ ktCommentSectionNames - 5130 + 4450 id - 5130 + 4450 name - 15 + 13 @@ -25664,7 +26075,7 @@ 1 2 - 5130 + 4450 @@ -25680,7 +26091,7 @@ 325 326 - 15 + 13 @@ -25690,15 +26101,15 @@ ktCommentSectionSubjectNames - 5130 + 4450 id - 5130 + 4450 subjectname - 3362 + 2916 @@ -25712,7 +26123,7 @@ 1 2 - 5130 + 4450 @@ -25728,22 +26139,22 @@ 1 2 - 2557 + 2218 2 3 - 505 + 438 3 9 - 252 + 219 10 16 - 47 + 41 @@ -25753,15 +26164,15 @@ ktCommentOwners - 85377 + 75329 id - 54008 + 47914 owner - 83356 + 73508 @@ -25775,22 +26186,22 @@ 1 2 - 34510 + 30838 2 3 - 12361 + 10872 3 4 - 4641 + 4025 4 6 - 2494 + 2177 @@ -25806,12 +26217,12 @@ 1 2 - 81335 + 71701 2 - 3 - 2020 + 4 + 1807 @@ -25821,19 +26232,19 @@ ktExtensionFunctions - 702451 + 1206297 id - 702451 + 1206297 typeid - 84403 + 97963 kttypeid - 1361 + 1920 @@ -25847,7 +26258,7 @@ 1 2 - 702451 + 1206297 @@ -25863,7 +26274,7 @@ 1 2 - 702451 + 1206297 @@ -25879,37 +26290,37 @@ 1 2 - 53092 + 55704 2 3 - 5445 + 5762 3 4 - 2722 - - - 4 - 5 - 6806 + 3841 5 - 12 - 6806 + 6 + 13445 - 12 - 69 - 6806 + 7 + 16 + 7683 - 84 - 174 - 2722 + 20 + 87 + 7683 + + + 109 + 227 + 3841 @@ -25925,7 +26336,7 @@ 1 2 - 84403 + 97963 @@ -25939,9 +26350,9 @@ 12 - 516 - 517 - 1361 + 628 + 629 + 1920 @@ -25955,9 +26366,9 @@ 12 - 62 - 63 - 1361 + 51 + 52 + 1920 @@ -25967,15 +26378,15 @@ ktProperties - 30236718 + 21895839 id - 30236718 + 21895839 nodeName - 10667458 + 13542035 @@ -25989,7 +26400,7 @@ 1 2 - 30236718 + 21895839 @@ -26005,22 +26416,17 @@ 1 2 - 7868544 + 11824790 2 - 3 - 1212953 + 4 + 1142909 - 3 - 8 - 807274 - - - 8 - 554 - 778686 + 4 + 352 + 574335 @@ -26030,15 +26436,15 @@ ktPropertyGetters - 4557765 + 5985387 id - 4557765 + 5985387 getter - 4557765 + 5985387 @@ -26052,7 +26458,7 @@ 1 2 - 4557765 + 5985387 @@ -26068,7 +26474,7 @@ 1 2 - 4557765 + 5985387 @@ -26078,15 +26484,15 @@ ktPropertySetters - 264099 + 366883 id - 264099 + 366883 setter - 264099 + 366883 @@ -26100,7 +26506,7 @@ 1 2 - 264099 + 366883 @@ -26116,7 +26522,7 @@ 1 2 - 264099 + 366883 @@ -26126,15 +26532,15 @@ ktPropertyBackingFields - 23552540 + 14423708 id - 23552540 + 14423708 backingField - 23552540 + 14423708 @@ -26148,7 +26554,7 @@ 1 2 - 23552540 + 14423708 @@ -26164,7 +26570,7 @@ 1 2 - 23552540 + 14423708 @@ -26174,15 +26580,15 @@ ktSyntheticBody - 10303 + 9108 id - 10303 + 9108 kind - 2060 + 1821 @@ -26196,7 +26602,7 @@ 1 2 - 10303 + 9108 @@ -26212,7 +26618,7 @@ 5 6 - 2060 + 1821 @@ -26222,37 +26628,37 @@ ktLocalFunction - 2722 + 3841 id - 2722 + 3841 ktInitializerAssignment - 392065 + 199475 id - 392065 + 199475 ktPropertyDelegates - 5650 + 5201 id - 5650 + 5201 variableId - 5650 + 5201 @@ -26266,7 +26672,7 @@ 1 2 - 5650 + 5201 @@ -26282,7 +26688,7 @@ 1 2 - 5650 + 5201 @@ -26292,15 +26698,15 @@ compiler_generated - 533645 + 1467534 id - 533645 + 1467534 kind - 5445 + 13445 @@ -26314,7 +26720,7 @@ 1 2 - 533645 + 1467534 @@ -26328,24 +26734,39 @@ 12 - 2 - 3 - 1361 + 1 + 2 + 1920 8 9 - 1361 + 1920 + + + 38 + 39 + 1920 81 82 - 1361 + 1920 - 301 - 302 - 1361 + 85 + 86 + 1920 + + + 236 + 237 + 1920 + + + 315 + 316 + 1920 @@ -26355,15 +26776,15 @@ ktFunctionOriginalNames - 1215676 + 1586627 id - 1215676 + 1586627 name - 138856 + 186323 @@ -26377,7 +26798,7 @@ 1 2 - 1215676 + 1586627 @@ -26393,22 +26814,22 @@ 1 2 - 111629 + 147905 2 - 7 - 10890 + 4 + 13445 - 7 - 31 - 10890 + 6 + 16 + 15366 - 92 - 380 - 5445 + 22 + 339 + 9604 @@ -26418,11 +26839,11 @@ ktDataClasses - 80319 + 113330 id - 80319 + 113330 diff --git a/java/ql/lib/qlpack.yml b/java/ql/lib/qlpack.yml index 0d8258e5ef1..4b103c629a2 100644 --- a/java/ql/lib/qlpack.yml +++ b/java/ql/lib/qlpack.yml @@ -5,3 +5,5 @@ dbscheme: config/semmlecode.dbscheme extractor: java library: true upgrades: upgrades +dependencies: + codeql/regex: ${workspace} \ No newline at end of file diff --git a/java/ql/lib/semmle/code/java/Collections.qll b/java/ql/lib/semmle/code/java/Collections.qll index 05e8ffbe7e4..b890c8545e5 100644 --- a/java/ql/lib/semmle/code/java/Collections.qll +++ b/java/ql/lib/semmle/code/java/Collections.qll @@ -6,8 +6,8 @@ import java /** - * The type `t` is a parameterization of `g`, where the `i`-th type parameter of - * `g` is instantiated to `a`? + * Holds if the type `t` is a parameterization of `g`, where the `i`-th type parameter of + * `g` is instantiated to `arg`. * * For example, `List` parameterizes `List`, instantiating its `0`-th * type parameter to `Integer`, while the raw type `List` also parameterizes diff --git a/java/ql/lib/semmle/code/java/Compilation.qll b/java/ql/lib/semmle/code/java/Compilation.qll index f38dc8ddb6b..c4b846edade 100644 --- a/java/ql/lib/semmle/code/java/Compilation.qll +++ b/java/ql/lib/semmle/code/java/Compilation.qll @@ -143,4 +143,9 @@ class Compilation extends @compilation { * Holds if the extractor encountered non-recoverable errors. */ predicate nonRecoverableErrors() { compilation_finished(this, _, _, 2) } + + /** + * Gets the piece of compilation information with the given key, if any. + */ + string getInfo(string key) { compilation_info(this, key, result) } } diff --git a/java/ql/lib/semmle/code/java/Generics.qll b/java/ql/lib/semmle/code/java/Generics.qll index 54cab14fe40..affc0132f94 100644 --- a/java/ql/lib/semmle/code/java/Generics.qll +++ b/java/ql/lib/semmle/code/java/Generics.qll @@ -195,6 +195,9 @@ class TypeVariable extends BoundedType, Modifiable, @typevariable { result = this.getASuppliedType().(TypeVariable).getAnUltimatelySuppliedType() } + /** Gets the index of `this` type variable. */ + int getIndex() { typeVars(this, _, result, _, _) } + override string getAPrimaryQlClass() { result = "TypeVariable" } } diff --git a/java/ql/lib/semmle/code/java/PrintAst.qll b/java/ql/lib/semmle/code/java/PrintAst.qll index 4fe7e3f1ec5..4df23f4ec14 100644 --- a/java/ql/lib/semmle/code/java/PrintAst.qll +++ b/java/ql/lib/semmle/code/java/PrintAst.qll @@ -7,7 +7,7 @@ */ import java -import semmle.code.java.regex.RegexTreeView +import semmle.code.java.regex.RegexTreeView as RegexTreeView private newtype TPrintAstConfiguration = MkPrintAstConfiguration() @@ -134,8 +134,10 @@ private newtype TPrintAstNode = TImportsNode(CompilationUnit cu) { shouldPrint(cu, _) and exists(Import i | i.getCompilationUnit() = cu) } or - TRegExpTermNode(RegExpTerm term) { - exists(StringLiteral str | term.getRootTerm() = getParsedRegExp(str) and shouldPrint(str, _)) + TRegExpTermNode(RegexTreeView::RegExpTerm term) { + exists(StringLiteral str | + term.getRootTerm() = RegexTreeView::getParsedRegExp(str) and shouldPrint(str, _) + ) } /** @@ -316,7 +318,7 @@ final class StringLiteralNode extends ExprStmtNode { override PrintAstNode getChild(int childIndex) { childIndex = 0 and - result.(RegExpTermNode).getTerm() = getParsedRegExp(element) + result.(RegExpTermNode).getTerm() = RegexTreeView::getParsedRegExp(element) } } @@ -324,12 +326,12 @@ final class StringLiteralNode extends ExprStmtNode { * A node representing a regular expression term. */ class RegExpTermNode extends TRegExpTermNode, PrintAstNode { - RegExpTerm term; + RegexTreeView::RegExpTerm term; RegExpTermNode() { this = TRegExpTermNode(term) } /** Gets the `RegExpTerm` for this node. */ - RegExpTerm getTerm() { result = term } + RegexTreeView::RegExpTerm getTerm() { result = term } override PrintAstNode getChild(int childIndex) { result.(RegExpTermNode).getTerm() = term.getChild(childIndex) diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll index dde16ab5a2a..f681e90aa21 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll @@ -136,6 +136,18 @@ module Consistency { msg = "Local flow step does not preserve enclosing callable." } + query predicate readStepIsLocal(Node n1, Node n2, string msg) { + readStep(n1, _, n2) and + nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and + msg = "Read step does not preserve enclosing callable." + } + + query predicate storeStepIsLocal(Node n1, Node n2, string msg) { + storeStep(n1, _, n2) and + nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and + msg = "Store step does not preserve enclosing callable." + } + private DataFlowType typeRepr() { result = getNodeType(_) } query predicate compatibleTypesReflexive(DataFlowType t, string msg) { diff --git a/java/ql/lib/semmle/code/java/regex/RegexTreeView.qll b/java/ql/lib/semmle/code/java/regex/RegexTreeView.qll index 14663327103..2eecff1e627 100644 --- a/java/ql/lib/semmle/code/java/regex/RegexTreeView.qll +++ b/java/ql/lib/semmle/code/java/regex/RegexTreeView.qll @@ -1,7 +1,19 @@ /** Provides a class hierarchy corresponding to a parse tree of regular expressions. */ -private import java -private import semmle.code.java.regex.regex +private import semmle.code.java.regex.regex as RE // importing under a namescape to avoid naming conflict for `Top`. +private import codeql.regex.nfa.NfaUtils as NfaUtils +// exporting as RegexTreeView, and in the top-level scope. +import Impl as RegexTreeView +import Impl + +/** Gets the parse tree resulting from parsing `re`, if such has been constructed. */ +RegExpTerm getParsedRegExp(RE::StringLiteral re) { result.getRegex() = re and result.isRootTerm() } + +private class Regex = RE::Regex; + +private class Location = RE::Location; + +private class File = RE::File; /** * An element containing a regular expression term, that is, either @@ -49,1038 +61,1114 @@ private newtype TRegExpParent = /** A back reference */ TRegExpBackRef(Regex re, int start, int end) { re.backreference(start, end) } -/** - * An element containing a regular expression term, that is, either - * a string literal (parsed as a regular expression; the root of the parse tree) - * or another regular expression term (a descendant of the root). - */ -class RegExpParent extends TRegExpParent { - /** Gets a textual representation of this element. */ - string toString() { result = "RegExpParent" } +private import codeql.regex.RegexTreeView - /** Gets the `i`th child term. */ - RegExpTerm getChild(int i) { none() } +/** An implementation that statisfies the RegexTreeView signature. */ +module Impl implements RegexTreeViewSig { + /** + * An element containing a regular expression term, that is, either + * a string literal (parsed as a regular expression; the root of the parse tree) + * or another regular expression term (a descendant of the root). + */ + class RegExpParent extends TRegExpParent { + /** Gets a textual representation of this element. */ + string toString() { result = "RegExpParent" } - /** Gets a child term . */ - RegExpTerm getAChild() { result = this.getChild(_) } + /** Gets the `i`th child term. */ + RegExpTerm getChild(int i) { none() } - /** Gets the number of child terms. */ - int getNumChild() { result = count(this.getAChild()) } + /** Gets a child term . */ + RegExpTerm getAChild() { result = this.getChild(_) } - /** Gets the associated regex. */ - abstract Regex getRegex(); -} + /** Gets the number of child terms. */ + int getNumChild() { result = count(this.getAChild()) } -/** - * A string literal used as a regular expression. - * - * As an optimisation, only regexes containing an infinite repitition quatifier (`+`, `*`, or `{x,}`) - * and therefore may be relevant for ReDoS queries are considered. - */ -class RegExpLiteral extends TRegExpLiteral, RegExpParent { - Regex re; - - RegExpLiteral() { this = TRegExpLiteral(re) } - - override string toString() { result = re.toString() } - - override RegExpTerm getChild(int i) { i = 0 and result.getRegex() = re and result.isRootTerm() } - - /** Holds if dot, `.`, matches all characters, including newlines. */ - predicate isDotAll() { re.getAMode() = "DOTALL" } - - /** Holds if this regex matching is case-insensitive for this regex. */ - predicate isIgnoreCase() { re.getAMode() = "IGNORECASE" } - - /** Get a string representing all modes for this regex. */ - string getFlags() { result = concat(string mode | mode = re.getAMode() | mode, " | ") } - - override Regex getRegex() { result = re } - - /** Gets the primary QL class for this regex. */ - string getPrimaryQLClass() { result = "RegExpLiteral" } -} - -/** - * A regular expression term, that is, a syntactic part of a regular expression. - * These are the tree nodes that form the parse tree of a regular expression literal. - */ -class RegExpTerm extends RegExpParent { - Regex re; - int start; - int end; - - RegExpTerm() { - this = TRegExpAlt(re, start, end) - or - this = TRegExpBackRef(re, start, end) - or - this = TRegExpCharacterClass(re, start, end) - or - this = TRegExpCharacterRange(re, start, end) - or - this = TRegExpNormalChar(re, start, end) - or - this = TRegExpQuote(re, start, end) - or - this = TRegExpGroup(re, start, end) - or - this = TRegExpQuantifier(re, start, end) - or - this = TRegExpSequence(re, start, end) - or - this = TRegExpSpecialChar(re, start, end) + /** Gets the associated regex. */ + abstract Regex getRegex(); } /** - * Gets the outermost term of this regular expression. + * A string literal used as a regular expression. + * + * As an optimisation, only regexes containing an infinite repitition quatifier (`+`, `*`, or `{x,}`) + * and therefore may be relevant for ReDoS queries are considered. */ - RegExpTerm getRootTerm() { - this.isRootTerm() and result = this - or - result = this.getParent().(RegExpTerm).getRootTerm() + class RegExpLiteral extends TRegExpLiteral, RegExpParent { + Regex re; + + RegExpLiteral() { this = TRegExpLiteral(re) } + + override string toString() { result = re.toString() } + + override RegExpTerm getChild(int i) { i = 0 and result.getRegex() = re and result.isRootTerm() } + + /** Holds if dot, `.`, matches all characters, including newlines. */ + predicate isDotAll() { re.getAMode() = "DOTALL" } + + /** Holds if this regex matching is case-insensitive for this regex. */ + predicate isIgnoreCase() { re.getAMode() = "IGNORECASE" } + + /** Get a string representing all modes for this regex. */ + string getFlags() { result = concat(string mode | mode = re.getAMode() | mode, " | ") } + + override Regex getRegex() { result = re } + + /** Gets the primary QL class for this regex. */ + string getPrimaryQLClass() { result = "RegExpLiteral" } } /** - * Holds if this term is part of a string literal - * that is interpreted as a regular expression. + * A regular expression term, that is, a syntactic part of a regular expression. + * These are the tree nodes that form the parse tree of a regular expression literal. */ - predicate isUsedAsRegExp() { any() } + class RegExpTerm extends RegExpParent { + Regex re; + int start; + int end; - /** - * Holds if this is the root term of a regular expression. - */ - predicate isRootTerm() { start = 0 and end = re.getText().length() } + RegExpTerm() { + this = TRegExpAlt(re, start, end) + or + this = TRegExpBackRef(re, start, end) + or + this = TRegExpCharacterClass(re, start, end) + or + this = TRegExpCharacterRange(re, start, end) + or + this = TRegExpNormalChar(re, start, end) + or + this = TRegExpQuote(re, start, end) + or + this = TRegExpGroup(re, start, end) + or + this = TRegExpQuantifier(re, start, end) + or + this = TRegExpSequence(re, start, end) + or + this = TRegExpSpecialChar(re, start, end) + } - /** - * Gets the parent term of this regular expression term, or the - * regular expression literal if this is the root term. - */ - RegExpParent getParent() { result.getAChild() = this } - - override Regex getRegex() { result = re } - - /** Gets the offset at which this term starts. */ - int getStart() { result = start } - - /** Gets the offset at which this term ends. */ - int getEnd() { result = end } - - /** Holds if this term occurs in regex `inRe` offsets `startOffset` to `endOffset`. */ - predicate occursInRegex(Regex inRe, int startOffset, int endOffset) { - inRe = re and startOffset = start and endOffset = end - } - - override string toString() { result = re.getText().substring(start, end) } - - /** - * Gets the location of the surrounding regex, as locations inside the regex do not exist. - * To get location information corresponding to the term inside the regex, - * use `hasLocationInfo`. - */ - Location getLocation() { result = re.getLocation() } - - /** Holds if this term is found at the specified location offsets. */ - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - /* - * This is an approximation that handles the simple and common case of single, - * normal string literal written in the source, but does not give correct results in more complex cases - * such as compile-time concatenation, or multi-line string literals. + /** + * Gets the outermost term of this regular expression. */ - - exists(int re_start, int re_end, int src_start, int src_end | - re.getLocation().hasLocationInfo(filepath, startline, re_start, endline, re_end) and - re.sourceCharacter(start, src_start, _) and - re.sourceCharacter(end - 1, _, src_end) and - startcolumn = re_start + src_start and - endcolumn = re_start + src_end - 1 - ) - } - - /** Gets the file in which this term is found. */ - File getFile() { result = this.getLocation().getFile() } - - /** Gets the raw source text of this term. */ - string getRawValue() { result = this.toString() } - - /** Gets the string literal in which this term is found. */ - RegExpLiteral getLiteral() { result = TRegExpLiteral(re) } - - /** Gets the regular expression term that is matched (textually) before this one, if any. */ - RegExpTerm getPredecessor() { - exists(RegExpTerm parent | parent = this.getParent() | - result = parent.(RegExpSequence).previousElement(this) + RegExpTerm getRootTerm() { + this.isRootTerm() and result = this or - not exists(parent.(RegExpSequence).previousElement(this)) and - not parent instanceof RegExpSubPattern and - result = parent.getPredecessor() - ) + result = this.getParent().(RegExpTerm).getRootTerm() + } + + /** + * Holds if this term is part of a string literal + * that is interpreted as a regular expression. + */ + predicate isUsedAsRegExp() { any() } + + /** + * Holds if this is the root term of a regular expression. + */ + predicate isRootTerm() { start = 0 and end = re.getText().length() } + + /** + * Gets the parent term of this regular expression term, or the + * regular expression literal if this is the root term. + */ + RegExpParent getParent() { result.getAChild() = this } + + override Regex getRegex() { result = re } + + /** Gets the offset at which this term starts. */ + int getStart() { result = start } + + /** Gets the offset at which this term ends. */ + int getEnd() { result = end } + + /** Holds if this term occurs in regex `inRe` offsets `startOffset` to `endOffset`. */ + predicate occursInRegex(Regex inRe, int startOffset, int endOffset) { + inRe = re and startOffset = start and endOffset = end + } + + override string toString() { result = re.getText().substring(start, end) } + + /** + * Gets the location of the surrounding regex, as locations inside the regex do not exist. + * To get location information corresponding to the term inside the regex, + * use `hasLocationInfo`. + */ + Location getLocation() { result = re.getLocation() } + + /** Holds if this term is found at the specified location offsets. */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + /* + * This is an approximation that handles the simple and common case of single, + * normal string literal written in the source, but does not give correct results in more complex cases + * such as compile-time concatenation, or multi-line string literals. + */ + + exists(int re_start, int re_end, int src_start, int src_end | + re.getLocation().hasLocationInfo(filepath, startline, re_start, endline, re_end) and + re.sourceCharacter(start, src_start, _) and + re.sourceCharacter(end - 1, _, src_end) and + startcolumn = re_start + src_start and + endcolumn = re_start + src_end - 1 + ) + } + + /** Gets the file in which this term is found. */ + File getFile() { result = this.getLocation().getFile() } + + /** Gets the raw source text of this term. */ + string getRawValue() { result = this.toString() } + + /** Gets the string literal in which this term is found. */ + RegExpLiteral getLiteral() { result = TRegExpLiteral(re) } + + /** Gets the regular expression term that is matched (textually) before this one, if any. */ + RegExpTerm getPredecessor() { + exists(RegExpTerm parent | parent = this.getParent() | + result = parent.(RegExpSequence).previousElement(this) + or + not exists(parent.(RegExpSequence).previousElement(this)) and + not parent instanceof RegExpSubPattern and + result = parent.getPredecessor() + ) + } + + /** Gets the regular expression term that is matched (textually) after this one, if any. */ + RegExpTerm getSuccessor() { + exists(RegExpTerm parent | parent = this.getParent() | + result = parent.(RegExpSequence).nextElement(this) + or + not exists(parent.(RegExpSequence).nextElement(this)) and + not parent instanceof RegExpSubPattern and + result = parent.getSuccessor() + ) + } + + /** Gets the primary QL class for this term. */ + string getPrimaryQLClass() { result = "RegExpTerm" } } - /** Gets the regular expression term that is matched (textually) after this one, if any. */ - RegExpTerm getSuccessor() { - exists(RegExpTerm parent | parent = this.getParent() | - result = parent.(RegExpSequence).nextElement(this) - or - not exists(parent.(RegExpSequence).nextElement(this)) and - not parent instanceof RegExpSubPattern and - result = parent.getSuccessor() - ) - } - - /** Gets the primary QL class for this term. */ - string getPrimaryQLClass() { result = "RegExpTerm" } -} - -/** - * A quantified regular expression term. - * - * Example: - * - * ``` - * ((ECMA|Java)[sS]cript)* - * ``` - */ -class RegExpQuantifier extends RegExpTerm, TRegExpQuantifier { - int part_end; - boolean maybe_empty; - boolean may_repeat_forever; - - RegExpQuantifier() { - this = TRegExpQuantifier(re, start, end) and - re.quantifiedPart(start, part_end, end, maybe_empty, may_repeat_forever) - } - - override RegExpTerm getChild(int i) { - i = 0 and - result.occursInRegex(re, start, part_end) - } - - /** Holds if this term may match zero times. */ - predicate mayBeEmpty() { maybe_empty = true } - - /** Holds if this term may match an unlimited number of times. */ - predicate mayRepeatForever() { may_repeat_forever = true } - - /** Gets the quantifier for this term. That is e.g "?" for "a?". */ - string getQuantifier() { result = re.getText().substring(part_end, end) } - - /** Holds if this is a possessive quantifier, e.g. a*+. */ - predicate isPossessive() { - exists(string q | q = this.getQuantifier() | q.length() > 1 and q.charAt(q.length() - 1) = "+") - } - - override string getPrimaryQLClass() { result = "RegExpQuantifier" } -} - -/** - * A regular expression term that permits unlimited repetitions. - */ -class InfiniteRepetitionQuantifier extends RegExpQuantifier { - InfiniteRepetitionQuantifier() { this.mayRepeatForever() } -} - -/** - * A star-quantified term. - * - * Example: - * - * ``` - * \w* - * ``` - */ -class RegExpStar extends InfiniteRepetitionQuantifier { - RegExpStar() { this.getQuantifier().charAt(0) = "*" } - - override string getPrimaryQLClass() { result = "RegExpStar" } -} - -/** - * A plus-quantified term. - * - * Example: - * - * ``` - * \w+ - * ``` - */ -class RegExpPlus extends InfiniteRepetitionQuantifier { - RegExpPlus() { this.getQuantifier().charAt(0) = "+" } - - override string getPrimaryQLClass() { result = "RegExpPlus" } -} - -/** - * An optional term. - * - * Example: - * - * ``` - * ;? - * ``` - */ -class RegExpOpt extends RegExpQuantifier { - RegExpOpt() { this.getQuantifier().charAt(0) = "?" } - - override string getPrimaryQLClass() { result = "RegExpOpt" } -} - -/** - * A range-quantified term - * - * Examples: - * - * ``` - * \w{2,4} - * \w{2,} - * \w{2} - * ``` - */ -class RegExpRange extends RegExpQuantifier { - string upper; - string lower; - - RegExpRange() { re.multiples(part_end, end, lower, upper) } - - /** Gets the string defining the upper bound of this range, which is empty when no such bound exists. */ - string getUpper() { result = upper } - - /** Gets the string defining the lower bound of this range, which is empty when no such bound exists. */ - string getLower() { result = lower } - /** - * Gets the upper bound of the range, if any. + * A quantified regular expression term. * - * If there is no upper bound, any number of repetitions is allowed. - * For a term of the form `r{lo}`, both the lower and the upper bound - * are `lo`. + * Example: + * + * ``` + * ((ECMA|Java)[sS]cript)* + * ``` */ - int getUpperBound() { result = this.getUpper().toInt() } + class RegExpQuantifier extends RegExpTerm, TRegExpQuantifier { + int part_end; + boolean maybe_empty; + boolean may_repeat_forever; - /** Gets the lower bound of the range. */ - int getLowerBound() { result = this.getLower().toInt() } + RegExpQuantifier() { + this = TRegExpQuantifier(re, start, end) and + re.quantifiedPart(start, part_end, end, maybe_empty, may_repeat_forever) + } - override string getPrimaryQLClass() { result = "RegExpRange" } -} - -/** - * A sequence term. - * - * Example: - * - * ``` - * (ECMA|Java)Script - * ``` - * - * This is a sequence with the elements `(ECMA|Java)` and `Script`. - */ -class RegExpSequence extends RegExpTerm, TRegExpSequence { - RegExpSequence() { this = TRegExpSequence(re, start, end) } - - override RegExpTerm getChild(int i) { result = seqChild(re, start, end, i) } - - /** Gets the element preceding `element` in this sequence. */ - RegExpTerm previousElement(RegExpTerm element) { element = this.nextElement(result) } - - /** Gets the element following `element` in this sequence. */ - RegExpTerm nextElement(RegExpTerm element) { - exists(int i | - element = this.getChild(i) and - result = this.getChild(i + 1) - ) - } - - override string getPrimaryQLClass() { result = "RegExpSequence" } -} - -pragma[nomagic] -private int seqChildEnd(Regex re, int start, int end, int i) { - result = seqChild(re, start, end, i).getEnd() -} - -// moved out so we can use it in the charpred -private RegExpTerm seqChild(Regex re, int start, int end, int i) { - re.sequence(start, end) and - ( - i = 0 and - exists(int itemEnd | - re.item(start, itemEnd) and - result.occursInRegex(re, start, itemEnd) - ) - or - i > 0 and - exists(int itemStart, int itemEnd | itemStart = seqChildEnd(re, start, end, i - 1) | - re.item(itemStart, itemEnd) and - result.occursInRegex(re, itemStart, itemEnd) - ) - ) -} - -/** - * An alternative term, that is, a term of the form `a|b`. - * - * Example: - * - * ``` - * ECMA|Java - * ``` - */ -class RegExpAlt extends RegExpTerm, TRegExpAlt { - RegExpAlt() { this = TRegExpAlt(re, start, end) } - - override RegExpTerm getChild(int i) { - i = 0 and - exists(int part_end | - re.alternationOption(start, end, start, part_end) and + override RegExpTerm getChild(int i) { + i = 0 and result.occursInRegex(re, start, part_end) - ) - or - i > 0 and - exists(int part_start, int part_end | - part_start = this.getChild(i - 1).getEnd() + 1 // allow for the | - | - re.alternationOption(start, end, part_start, part_end) and - result.occursInRegex(re, part_start, part_end) - ) + } + + /** Holds if this term may match zero times. */ + predicate mayBeEmpty() { maybe_empty = true } + + /** Holds if this term may match an unlimited number of times. */ + predicate mayRepeatForever() { may_repeat_forever = true } + + /** Gets the quantifier for this term. That is e.g "?" for "a?". */ + string getQuantifier() { result = re.getText().substring(part_end, end) } + + /** Holds if this is a possessive quantifier, e.g. a*+. */ + predicate isPossessive() { + exists(string q | q = this.getQuantifier() | + q.length() > 1 and q.charAt(q.length() - 1) = "+" + ) + } + + override string getPrimaryQLClass() { result = "RegExpQuantifier" } } - override string getPrimaryQLClass() { result = "RegExpAlt" } -} - -/** - * An escaped regular expression term, that is, a regular expression - * term starting with a backslash, which is not a backreference. - * - * Example: - * - * ``` - * \. - * \w - * ``` - */ -class RegExpEscape extends RegExpNormalChar { - RegExpEscape() { re.escapedCharacter(start, end) } - /** - * Gets the name of the escaped; for example, `w` for `\w`. - * TODO: Handle named escapes. + * A regular expression term that permits unlimited repetitions. */ - override string getValue() { - not this.isUnicode() and - this.isIdentityEscape() and - result = this.getUnescaped() - or - this.getUnescaped() = "n" and result = "\n" - or - this.getUnescaped() = "r" and result = "\r" - or - this.getUnescaped() = "t" and result = "\t" - or - this.getUnescaped() = "f" and result = 12.toUnicode() // form feed - or - this.getUnescaped() = "a" and result = 7.toUnicode() // alert/bell - or - this.getUnescaped() = "e" and result = 27.toUnicode() // escape (0x1B) - or - this.isUnicode() and - result = this.getUnicode() - } - - /** Holds if this terms name is given by the part following the escape character. */ - predicate isIdentityEscape() { not this.getUnescaped() in ["n", "r", "t", "f", "a", "e"] } - - override string getPrimaryQLClass() { result = "RegExpEscape" } - - /** Gets the part of the term following the escape character. That is e.g. "w" if the term is "\w". */ - private string getUnescaped() { result = this.getText().suffix(1) } - - /** - * Gets the text for this escape. That is e.g. "\w". - */ - private string getText() { result = re.getText().substring(start, end) } - - /** - * Holds if this is a unicode escape. - */ - private predicate isUnicode() { this.getText().matches(["\\u%", "\\x%"]) } - - /** - * Gets the unicode char for this escape. - * E.g. for `\u0061` this returns "a". - */ - private string getUnicode() { - exists(int codepoint | codepoint = sum(this.getHexValueFromUnicode(_)) | - result = codepoint.toUnicode() - ) - } - - /** Gets the part of this escape that is a hexidecimal string */ - private string getHexString() { - this.isUnicode() and - if this.getText().matches("\\u%") // \uhhhh - then result = this.getText().suffix(2) - else - if this.getText().matches("\\x{%") // \x{h..h} - then result = this.getText().substring(3, this.getText().length() - 1) - else result = this.getText().suffix(2) // \xhh + class InfiniteRepetitionQuantifier extends RegExpQuantifier { + InfiniteRepetitionQuantifier() { this.mayRepeatForever() } } /** - * Gets int value for the `index`th char in the hex number of the unicode escape. - * E.g. for `\u0061` and `index = 2` this returns 96 (the number `6` interpreted as hex). - */ - private int getHexValueFromUnicode(int index) { - this.isUnicode() and - exists(string hex, string char | hex = this.getHexString() | - char = hex.charAt(index) and - result = 16.pow(hex.length() - index - 1) * toHex(char) - ) - } -} - -/** - * Gets the hex number for the `hex` char. - */ -private int toHex(string hex) { - result = [0 .. 9] and hex = result.toString() - or - result = 10 and hex = ["a", "A"] - or - result = 11 and hex = ["b", "B"] - or - result = 12 and hex = ["c", "C"] - or - result = 13 and hex = ["d", "D"] - or - result = 14 and hex = ["e", "E"] - or - result = 15 and hex = ["f", "F"] -} - -/** - * A character class escape in a regular expression. - * That is, an escaped character that denotes multiple characters. - * - * Examples: - * - * ``` - * \w - * \S - * ``` - */ -class RegExpCharacterClassEscape extends RegExpEscape { - RegExpCharacterClassEscape() { - this.getValue() in ["d", "D", "s", "S", "w", "W", "h", "H", "v", "V"] or - this.getValue().charAt(0) in ["p", "P"] - } - - override RegExpTerm getChild(int i) { none() } - - override string getPrimaryQLClass() { result = "RegExpCharacterClassEscape" } -} - -/** - * A named character class in a regular expression. - * - * Examples: - * - * ``` - * \p{Digit} - * \p{IsLowerCase} - */ -class RegExpNamedProperty extends RegExpCharacterClassEscape { - boolean inverted; - string name; - - RegExpNamedProperty() { - name = this.getValue().substring(2, this.getValue().length() - 1) and - ( - inverted = false and - this.getValue().charAt(0) = "p" - or - inverted = true and - this.getValue().charAt(0) = "P" - ) - } - - /** Holds if this class is inverted. */ - predicate isInverted() { inverted = true } - - /** Gets the name of this class. */ - string getClassName() { result = name } - - /** - * Gets an equivalent single-chcracter escape sequence for this class (e.g. \d) if possible, excluding the escape character. - */ - string getBackslashEquivalent() { - exists(string eq | if inverted = true then result = eq.toUpperCase() else result = eq | - name = ["Digit", "IsDigit"] and - eq = "d" - or - name = ["Space", "IsWhite_Space"] and - eq = "s" - ) - } -} - -/** - * A character class in a regular expression. - * - * Examples: - * - * ``` - * [a-z_] - * [^<>&] - * ``` - */ -class RegExpCharacterClass extends RegExpTerm, TRegExpCharacterClass { - RegExpCharacterClass() { this = TRegExpCharacterClass(re, start, end) } - - /** Holds if this character class is inverted, matching the opposite of its content. */ - predicate isInverted() { re.getChar(start + 1) = "^" } - - /** Holds if this character class can match anything. */ - predicate isUniversalClass() { - // [^] - this.isInverted() and not exists(this.getAChild()) - or - // [\w\W] and similar - not this.isInverted() and - exists(string cce1, string cce2 | - cce1 = this.getAChild().(RegExpCharacterClassEscape).getValue() and - cce2 = this.getAChild().(RegExpCharacterClassEscape).getValue() - | - cce1 != cce2 and cce1.toLowerCase() = cce2.toLowerCase() - ) - } - - override RegExpTerm getChild(int i) { - i = 0 and - exists(int itemStart, int itemEnd | - re.charSetStart(start, itemStart) and - re.charSetChild(start, itemStart, itemEnd) and - result.occursInRegex(re, itemStart, itemEnd) - ) - or - i > 0 and - exists(int itemStart, int itemEnd | itemStart = this.getChild(i - 1).getEnd() | - result.occursInRegex(re, itemStart, itemEnd) and - re.charSetChild(start, itemStart, itemEnd) - ) - } - - override string getPrimaryQLClass() { result = "RegExpCharacterClass" } -} - -/** - * A character range in a character class in a regular expression. - * - * Example: - * - * ``` - * a-z - * ``` - */ -class RegExpCharacterRange extends RegExpTerm, TRegExpCharacterRange { - int lower_end; - int upper_start; - - RegExpCharacterRange() { - this = TRegExpCharacterRange(re, start, end) and - re.charRange(_, start, lower_end, upper_start, end) - } - - /** Holds if this range goes from `lo` to `hi`, in effect is `lo-hi`. */ - predicate isRange(string lo, string hi) { - lo = re.getText().substring(start, lower_end) and - hi = re.getText().substring(upper_start, end) - } - - override RegExpTerm getChild(int i) { - i = 0 and - result.occursInRegex(re, start, lower_end) - or - i = 1 and - result.occursInRegex(re, upper_start, end) - } - - override string getPrimaryQLClass() { result = "RegExpCharacterRange" } -} - -/** - * A normal character in a regular expression, that is, a character - * without special meaning. This includes escaped characters. - * It also includes escape sequences that represent character classes. - * - * Examples: - * ``` - * t - * \t - * ``` - */ -class RegExpNormalChar extends RegExpTerm, TRegExpNormalChar { - RegExpNormalChar() { this = TRegExpNormalChar(re, start, end) } - - /** - * Holds if this constant represents a valid Unicode character (as opposed - * to a surrogate code point that does not correspond to a character by itself.) - */ - predicate isCharacter() { any() } - - /** Gets the string representation of the char matched by this term. */ - string getValue() { result = re.getText().substring(start, end) } - - override RegExpTerm getChild(int i) { none() } - - override string getPrimaryQLClass() { result = "RegExpNormalChar" } -} - -/** - * A quoted sequence. - * - * Example: - * ``` - * \Qabc\E - * ``` - */ -class RegExpQuote extends RegExpTerm, TRegExpQuote { - string value; - - RegExpQuote() { - exists(int inner_start, int inner_end | - this = TRegExpQuote(re, start, end) and - re.quote(start, end, inner_start, inner_end) and - value = re.getText().substring(inner_start, inner_end) - ) - } - - /** Gets the string matched by this quote term. */ - string getValue() { result = value } - - override string getPrimaryQLClass() { result = "RegExpQuote" } -} - -/** - * A constant regular expression term, that is, a regular expression - * term matching a single string. This can be a single character or a quoted sequence. - * - * Example: - * - * ``` - * a - * ``` - */ -class RegExpConstant extends RegExpTerm { - string value; - - RegExpConstant() { - (value = this.(RegExpNormalChar).getValue() or value = this.(RegExpQuote).getValue()) and - not this instanceof RegExpCharacterClassEscape - } - - /** - * Holds if this constant represents a valid Unicode character (as opposed - * to a surrogate code point that does not correspond to a character by itself.) - */ - predicate isCharacter() { any() } - - /** Gets the string matched by this constant term. */ - string getValue() { result = value } - - override RegExpTerm getChild(int i) { none() } - - override string getPrimaryQLClass() { result = "RegExpConstant" } -} - -/** - * A grouped regular expression. - * - * Examples: - * - * ``` - * (ECMA|Java) - * (?:ECMA|Java) - * (?['"]) - * ``` - */ -class RegExpGroup extends RegExpTerm, TRegExpGroup { - RegExpGroup() { this = TRegExpGroup(re, start, end) } - - /** - * Gets the index of this capture group within the enclosing regular - * expression literal. + * A star-quantified term. * - * For example, in the regular expression `/((a?).)(?:b)/`, the - * group `((a?).)` has index 1, the group `(a?)` nested inside it - * has index 2, and the group `(?:b)` has no index, since it is - * not a capture group. + * Example: + * + * ``` + * \w* + * ``` */ - int getNumber() { result = re.getGroupNumber(start, end) } + class RegExpStar extends InfiniteRepetitionQuantifier { + RegExpStar() { this.getQuantifier().charAt(0) = "*" } - /** Holds if this is a named capture group. */ - predicate isNamed() { exists(this.getName()) } + override string getPrimaryQLClass() { result = "RegExpStar" } + } - /** Gets the name of this capture group, if any. */ - string getName() { result = re.getGroupName(start, end) } + /** + * A plus-quantified term. + * + * Example: + * + * ``` + * \w+ + * ``` + */ + class RegExpPlus extends InfiniteRepetitionQuantifier { + RegExpPlus() { this.getQuantifier().charAt(0) = "+" } - override RegExpTerm getChild(int i) { - i = 0 and - exists(int in_start, int in_end | re.groupContents(start, end, in_start, in_end) | - result.occursInRegex(re, in_start, in_end) + override string getPrimaryQLClass() { result = "RegExpPlus" } + } + + /** + * An optional term. + * + * Example: + * + * ``` + * ;? + * ``` + */ + class RegExpOpt extends RegExpQuantifier { + RegExpOpt() { this.getQuantifier().charAt(0) = "?" } + + override string getPrimaryQLClass() { result = "RegExpOpt" } + } + + /** + * A range-quantified term + * + * Examples: + * + * ``` + * \w{2,4} + * \w{2,} + * \w{2} + * ``` + */ + class RegExpRange extends RegExpQuantifier { + string upper; + string lower; + + RegExpRange() { re.multiples(part_end, end, lower, upper) } + + /** Gets the string defining the upper bound of this range, which is empty when no such bound exists. */ + string getUpper() { result = upper } + + /** Gets the string defining the lower bound of this range, which is empty when no such bound exists. */ + string getLower() { result = lower } + + /** + * Gets the upper bound of the range, if any. + * + * If there is no upper bound, any number of repetitions is allowed. + * For a term of the form `r{lo}`, both the lower and the upper bound + * are `lo`. + */ + int getUpperBound() { result = this.getUpper().toInt() } + + /** Gets the lower bound of the range. */ + int getLowerBound() { result = this.getLower().toInt() } + + override string getPrimaryQLClass() { result = "RegExpRange" } + } + + /** + * A sequence term. + * + * Example: + * + * ``` + * (ECMA|Java)Script + * ``` + * + * This is a sequence with the elements `(ECMA|Java)` and `Script`. + */ + class RegExpSequence extends RegExpTerm, TRegExpSequence { + RegExpSequence() { this = TRegExpSequence(re, start, end) } + + override RegExpTerm getChild(int i) { result = seqChild(re, start, end, i) } + + /** Gets the element preceding `element` in this sequence. */ + RegExpTerm previousElement(RegExpTerm element) { element = this.nextElement(result) } + + /** Gets the element following `element` in this sequence. */ + RegExpTerm nextElement(RegExpTerm element) { + exists(int i | + element = this.getChild(i) and + result = this.getChild(i + 1) + ) + } + + override string getPrimaryQLClass() { result = "RegExpSequence" } + } + + pragma[nomagic] + private int seqChildEnd(Regex re, int start, int end, int i) { + result = seqChild(re, start, end, i).getEnd() + } + + // moved out so we can use it in the charpred + private RegExpTerm seqChild(Regex re, int start, int end, int i) { + re.sequence(start, end) and + ( + i = 0 and + exists(int itemEnd | + re.item(start, itemEnd) and + result.occursInRegex(re, start, itemEnd) + ) + or + i > 0 and + exists(int itemStart, int itemEnd | itemStart = seqChildEnd(re, start, end, i - 1) | + re.item(itemStart, itemEnd) and + result.occursInRegex(re, itemStart, itemEnd) + ) ) } - override string getPrimaryQLClass() { result = "RegExpGroup" } + /** + * An alternative term, that is, a term of the form `a|b`. + * + * Example: + * + * ``` + * ECMA|Java + * ``` + */ + class RegExpAlt extends RegExpTerm, TRegExpAlt { + RegExpAlt() { this = TRegExpAlt(re, start, end) } - /** Holds if this is the `n`th numbered group of literal `lit`. */ - predicate isNumberedGroupOfLiteral(RegExpLiteral lit, int n) { - lit = this.getLiteral() and n = this.getNumber() - } + override RegExpTerm getChild(int i) { + i = 0 and + exists(int part_end | + re.alternationOption(start, end, start, part_end) and + result.occursInRegex(re, start, part_end) + ) + or + i > 0 and + exists(int part_start, int part_end | + part_start = this.getChild(i - 1).getEnd() + 1 // allow for the | + | + re.alternationOption(start, end, part_start, part_end) and + result.occursInRegex(re, part_start, part_end) + ) + } - /** Holds if this is a group with name `name` of literal `lit`. */ - predicate isNamedGroupOfLiteral(RegExpLiteral lit, string name) { - lit = this.getLiteral() and name = this.getName() - } -} - -/** - * A special character in a regular expression. - * - * Examples: - * ``` - * ^ - * $ - * . - * ``` - */ -class RegExpSpecialChar extends RegExpTerm, TRegExpSpecialChar { - string char; - - RegExpSpecialChar() { - this = TRegExpSpecialChar(re, start, end) and - re.specialCharacter(start, end, char) + override string getPrimaryQLClass() { result = "RegExpAlt" } } /** - * Holds if this constant represents a valid Unicode character (as opposed - * to a surrogate code point that does not correspond to a character by itself.) + * An escaped regular expression term, that is, a regular expression + * term starting with a backslash, which is not a backreference. + * + * Example: + * + * ``` + * \. + * \w + * ``` */ - predicate isCharacter() { any() } + class RegExpEscape extends RegExpNormalChar { + RegExpEscape() { re.escapedCharacter(start, end) } - /** Gets the char for this term. */ - string getChar() { result = char } + /** + * Gets the name of the escaped; for example, `w` for `\w`. + * TODO: Handle named escapes. + */ + override string getValue() { + not this.isUnicode() and + this.isIdentityEscape() and + result = this.getUnescaped() + or + this.getUnescaped() = "n" and result = "\n" + or + this.getUnescaped() = "r" and result = "\r" + or + this.getUnescaped() = "t" and result = "\t" + or + this.getUnescaped() = "f" and result = 12.toUnicode() // form feed + or + this.getUnescaped() = "a" and result = 7.toUnicode() // alert/bell + or + this.getUnescaped() = "e" and result = 27.toUnicode() // escape (0x1B) + or + this.isUnicode() and + result = this.getUnicode() + } - override RegExpTerm getChild(int i) { none() } + /** Holds if this terms name is given by the part following the escape character. */ + predicate isIdentityEscape() { not this.getUnescaped() in ["n", "r", "t", "f", "a", "e"] } - override string getPrimaryQLClass() { result = "RegExpSpecialChar" } -} + override string getPrimaryQLClass() { result = "RegExpEscape" } -/** - * A dot regular expression. - * - * Example: - * - * ``` - * . - * ``` - */ -class RegExpDot extends RegExpSpecialChar { - RegExpDot() { this.getChar() = "." } + /** Gets the part of the term following the escape character. That is e.g. "w" if the term is "\w". */ + private string getUnescaped() { result = this.getText().suffix(1) } - override string getPrimaryQLClass() { result = "RegExpDot" } -} + /** + * Gets the text for this escape. That is e.g. "\w". + */ + private string getText() { result = re.getText().substring(start, end) } -/** - * A dollar assertion `$` matching the end of a line. - * - * Example: - * - * ``` - * $ - * ``` - */ -class RegExpDollar extends RegExpSpecialChar { - RegExpDollar() { this.getChar() = "$" } + /** + * Holds if this is a unicode escape. + */ + private predicate isUnicode() { this.getText().matches(["\\u%", "\\x%"]) } - override string getPrimaryQLClass() { result = "RegExpDollar" } -} + /** + * Gets the unicode char for this escape. + * E.g. for `\u0061` this returns "a". + */ + private string getUnicode() { + exists(int codepoint | codepoint = sum(this.getHexValueFromUnicode(_)) | + result = codepoint.toUnicode() + ) + } -/** - * A caret assertion `^` matching the beginning of a line. - * - * Example: - * - * ``` - * ^ - * ``` - */ -class RegExpCaret extends RegExpSpecialChar { - RegExpCaret() { this.getChar() = "^" } + /** Gets the part of this escape that is a hexidecimal string */ + private string getHexString() { + this.isUnicode() and + if this.getText().matches("\\u%") // \uhhhh + then result = this.getText().suffix(2) + else + if this.getText().matches("\\x{%") // \x{h..h} + then result = this.getText().substring(3, this.getText().length() - 1) + else result = this.getText().suffix(2) // \xhh + } - override string getPrimaryQLClass() { result = "RegExpCaret" } -} - -/** - * A zero-width match, that is, either an empty group or an assertion. - * - * Examples: - * ``` - * () - * (?=\w) - * ``` - */ -class RegExpZeroWidthMatch extends RegExpGroup { - RegExpZeroWidthMatch() { re.zeroWidthMatch(start, end) } - - override RegExpTerm getChild(int i) { none() } - - override string getPrimaryQLClass() { result = "RegExpZeroWidthMatch" } -} - -/** - * A zero-width lookahead or lookbehind assertion. - * - * Examples: - * - * ``` - * (?=\w) - * (?!\n) - * (?<=\.) - * (?` - * in a regular expression. - * - * Examples: - * - * ``` - * \1 - * (?P=quote) - * ``` - */ -class RegExpBackRef extends RegExpTerm, TRegExpBackRef { - RegExpBackRef() { this = TRegExpBackRef(re, start, end) } /** - * Gets the number of the capture group this back reference refers to, if any. + * A word boundary, that is, a regular expression term of the form `\b`. */ - int getNumber() { result = re.getBackrefNumber(start, end) } + class RegExpWordBoundary extends RegExpSpecialChar { + RegExpWordBoundary() { this.getChar() = "\\b" } + } /** - * Gets the name of the capture group this back reference refers to, if any. + * Gets the hex number for the `hex` char. */ - string getName() { result = re.getBackrefName(start, end) } - - /** Gets the capture group this back reference refers to. */ - RegExpGroup getGroup() { - result.isNumberedGroupOfLiteral(this.getLiteral(), this.getNumber()) + private int toHex(string hex) { + result = [0 .. 9] and hex = result.toString() or - result.isNamedGroupOfLiteral(this.getLiteral(), this.getName()) + result = 10 and hex = ["a", "A"] + or + result = 11 and hex = ["b", "B"] + or + result = 12 and hex = ["c", "C"] + or + result = 13 and hex = ["d", "D"] + or + result = 14 and hex = ["e", "E"] + or + result = 15 and hex = ["f", "F"] } - override RegExpTerm getChild(int i) { none() } + /** + * A character class escape in a regular expression. + * That is, an escaped character that denotes multiple characters. + * + * Examples: + * + * ``` + * \w + * \S + * ``` + */ + class RegExpCharacterClassEscape extends RegExpEscape { + RegExpCharacterClassEscape() { + this.getValue() in ["d", "D", "s", "S", "w", "W", "h", "H", "v", "V"] or + this.getValue().charAt(0) in ["p", "P"] + } - override string getPrimaryQLClass() { result = "RegExpBackRef" } + override RegExpTerm getChild(int i) { none() } + + override string getPrimaryQLClass() { result = "RegExpCharacterClassEscape" } + } + + /** + * A named character class in a regular expression. + * + * Examples: + * + * ``` + * \p{Digit} + * \p{IsLowerCase} + */ + class RegExpNamedProperty extends RegExpCharacterClassEscape { + boolean inverted; + string name; + + RegExpNamedProperty() { + name = this.getValue().substring(2, this.getValue().length() - 1) and + ( + inverted = false and + this.getValue().charAt(0) = "p" + or + inverted = true and + this.getValue().charAt(0) = "P" + ) + } + + /** Holds if this class is inverted. */ + predicate isInverted() { inverted = true } + + /** Gets the name of this class. */ + string getClassName() { result = name } + + /** + * Gets an equivalent single-chcracter escape sequence for this class (e.g. \d) if possible, excluding the escape character. + */ + string getBackslashEquivalent() { + exists(string eq | if inverted = true then result = eq.toUpperCase() else result = eq | + name = ["Digit", "IsDigit"] and + eq = "d" + or + name = ["Space", "IsWhite_Space"] and + eq = "s" + ) + } + } + + /** + * A character class in a regular expression. + * + * Examples: + * + * ``` + * [a-z_] + * [^<>&] + * ``` + */ + class RegExpCharacterClass extends RegExpTerm, TRegExpCharacterClass { + RegExpCharacterClass() { this = TRegExpCharacterClass(re, start, end) } + + /** Holds if this character class is inverted, matching the opposite of its content. */ + predicate isInverted() { re.getChar(start + 1) = "^" } + + /** Holds if this character class can match anything. */ + predicate isUniversalClass() { + // [^] + this.isInverted() and not exists(this.getAChild()) + or + // [\w\W] and similar + not this.isInverted() and + exists(string cce1, string cce2 | + cce1 = this.getAChild().(RegExpCharacterClassEscape).getValue() and + cce2 = this.getAChild().(RegExpCharacterClassEscape).getValue() + | + cce1 != cce2 and cce1.toLowerCase() = cce2.toLowerCase() + ) + } + + override RegExpTerm getChild(int i) { + i = 0 and + exists(int itemStart, int itemEnd | + re.charSetStart(start, itemStart) and + re.charSetChild(start, itemStart, itemEnd) and + result.occursInRegex(re, itemStart, itemEnd) + ) + or + i > 0 and + exists(int itemStart, int itemEnd | itemStart = this.getChild(i - 1).getEnd() | + result.occursInRegex(re, itemStart, itemEnd) and + re.charSetChild(start, itemStart, itemEnd) + ) + } + + override string getPrimaryQLClass() { result = "RegExpCharacterClass" } + } + + /** + * A character range in a character class in a regular expression. + * + * Example: + * + * ``` + * a-z + * ``` + */ + class RegExpCharacterRange extends RegExpTerm, TRegExpCharacterRange { + int lower_end; + int upper_start; + + RegExpCharacterRange() { + this = TRegExpCharacterRange(re, start, end) and + re.charRange(_, start, lower_end, upper_start, end) + } + + /** Holds if this range goes from `lo` to `hi`, in effect is `lo-hi`. */ + predicate isRange(string lo, string hi) { + lo = re.getText().substring(start, lower_end) and + hi = re.getText().substring(upper_start, end) + } + + override RegExpTerm getChild(int i) { + i = 0 and + result.occursInRegex(re, start, lower_end) + or + i = 1 and + result.occursInRegex(re, upper_start, end) + } + + override string getPrimaryQLClass() { result = "RegExpCharacterRange" } + } + + /** + * A normal character in a regular expression, that is, a character + * without special meaning. This includes escaped characters. + * It also includes escape sequences that represent character classes. + * + * Examples: + * ``` + * t + * \t + * ``` + */ + class RegExpNormalChar extends RegExpTerm, TRegExpNormalChar { + RegExpNormalChar() { this = TRegExpNormalChar(re, start, end) } + + /** + * Holds if this constant represents a valid Unicode character (as opposed + * to a surrogate code point that does not correspond to a character by itself.) + */ + predicate isCharacter() { any() } + + /** Gets the string representation of the char matched by this term. */ + string getValue() { result = re.getText().substring(start, end) } + + override RegExpTerm getChild(int i) { none() } + + override string getPrimaryQLClass() { result = "RegExpNormalChar" } + } + + /** + * A quoted sequence. + * + * Example: + * ``` + * \Qabc\E + * ``` + */ + class RegExpQuote extends RegExpTerm, TRegExpQuote { + string value; + + RegExpQuote() { + exists(int inner_start, int inner_end | + this = TRegExpQuote(re, start, end) and + re.quote(start, end, inner_start, inner_end) and + value = re.getText().substring(inner_start, inner_end) + ) + } + + /** Gets the string matched by this quote term. */ + string getValue() { result = value } + + override string getPrimaryQLClass() { result = "RegExpQuote" } + } + + /** + * A constant regular expression term, that is, a regular expression + * term matching a single string. This can be a single character or a quoted sequence. + * + * Example: + * + * ``` + * a + * ``` + */ + class RegExpConstant extends RegExpTerm { + string value; + + RegExpConstant() { + (value = this.(RegExpNormalChar).getValue() or value = this.(RegExpQuote).getValue()) and + not this instanceof RegExpCharacterClassEscape + } + + /** + * Holds if this constant represents a valid Unicode character (as opposed + * to a surrogate code point that does not correspond to a character by itself.) + */ + predicate isCharacter() { any() } + + /** Gets the string matched by this constant term. */ + string getValue() { result = value } + + override RegExpTerm getChild(int i) { none() } + + override string getPrimaryQLClass() { result = "RegExpConstant" } + } + + /** + * A grouped regular expression. + * + * Examples: + * + * ``` + * (ECMA|Java) + * (?:ECMA|Java) + * (?['"]) + * ``` + */ + class RegExpGroup extends RegExpTerm, TRegExpGroup { + RegExpGroup() { this = TRegExpGroup(re, start, end) } + + /** + * Gets the index of this capture group within the enclosing regular + * expression literal. + * + * For example, in the regular expression `/((a?).)(?:b)/`, the + * group `((a?).)` has index 1, the group `(a?)` nested inside it + * has index 2, and the group `(?:b)` has no index, since it is + * not a capture group. + */ + int getNumber() { result = re.getGroupNumber(start, end) } + + /** Holds if this is a named capture group. */ + predicate isNamed() { exists(this.getName()) } + + /** Gets the name of this capture group, if any. */ + string getName() { result = re.getGroupName(start, end) } + + override RegExpTerm getChild(int i) { + i = 0 and + exists(int in_start, int in_end | re.groupContents(start, end, in_start, in_end) | + result.occursInRegex(re, in_start, in_end) + ) + } + + override string getPrimaryQLClass() { result = "RegExpGroup" } + + /** Holds if this is the `n`th numbered group of literal `lit`. */ + predicate isNumberedGroupOfLiteral(RegExpLiteral lit, int n) { + lit = this.getLiteral() and n = this.getNumber() + } + + /** Holds if this is a group with name `name` of literal `lit`. */ + predicate isNamedGroupOfLiteral(RegExpLiteral lit, string name) { + lit = this.getLiteral() and name = this.getName() + } + } + + /** + * A special character in a regular expression. + * + * Examples: + * ``` + * ^ + * $ + * . + * ``` + */ + class RegExpSpecialChar extends RegExpTerm, TRegExpSpecialChar { + string char; + + RegExpSpecialChar() { + this = TRegExpSpecialChar(re, start, end) and + re.specialCharacter(start, end, char) + } + + /** + * Holds if this constant represents a valid Unicode character (as opposed + * to a surrogate code point that does not correspond to a character by itself.) + */ + predicate isCharacter() { any() } + + /** Gets the char for this term. */ + string getChar() { result = char } + + override RegExpTerm getChild(int i) { none() } + + override string getPrimaryQLClass() { result = "RegExpSpecialChar" } + } + + /** + * A dot regular expression. + * + * Example: + * + * ``` + * . + * ``` + */ + class RegExpDot extends RegExpSpecialChar { + RegExpDot() { this.getChar() = "." } + + override string getPrimaryQLClass() { result = "RegExpDot" } + } + + /** + * A dollar assertion `$` matching the end of a line. + * + * Example: + * + * ``` + * $ + * ``` + */ + class RegExpDollar extends RegExpSpecialChar { + RegExpDollar() { this.getChar() = "$" } + + override string getPrimaryQLClass() { result = "RegExpDollar" } + } + + /** + * A caret assertion `^` matching the beginning of a line. + * + * Example: + * + * ``` + * ^ + * ``` + */ + class RegExpCaret extends RegExpSpecialChar { + RegExpCaret() { this.getChar() = "^" } + + override string getPrimaryQLClass() { result = "RegExpCaret" } + } + + /** + * A zero-width match, that is, either an empty group or an assertion. + * + * Examples: + * ``` + * () + * (?=\w) + * ``` + */ + class RegExpZeroWidthMatch extends RegExpGroup { + RegExpZeroWidthMatch() { re.zeroWidthMatch(start, end) } + + override RegExpTerm getChild(int i) { none() } + + override string getPrimaryQLClass() { result = "RegExpZeroWidthMatch" } + } + + /** + * A zero-width lookahead or lookbehind assertion. + * + * Examples: + * + * ``` + * (?=\w) + * (?!\n) + * (?<=\.) + * (?` + * in a regular expression. + * + * Examples: + * + * ``` + * \1 + * (?P=quote) + * ``` + */ + class RegExpBackRef extends RegExpTerm, TRegExpBackRef { + RegExpBackRef() { this = TRegExpBackRef(re, start, end) } + + /** + * Gets the number of the capture group this back reference refers to, if any. + */ + int getNumber() { result = re.getBackrefNumber(start, end) } + + /** + * Gets the name of the capture group this back reference refers to, if any. + */ + string getName() { result = re.getBackrefName(start, end) } + + /** Gets the capture group this back reference refers to. */ + RegExpGroup getGroup() { + result.isNumberedGroupOfLiteral(this.getLiteral(), this.getNumber()) + or + result.isNamedGroupOfLiteral(this.getLiteral(), this.getName()) + } + + override RegExpTerm getChild(int i) { none() } + + override string getPrimaryQLClass() { result = "RegExpBackRef" } + } + + class Top = RegExpParent; + + /** + * Holds if `term` is an escape class representing e.g. `\d`. + * `clazz` is which character class it represents, e.g. "d" for `\d`. + */ + predicate isEscapeClass(RegExpTerm term, string clazz) { + term.(RegExpCharacterClassEscape).getValue() = clazz + or + term.(RegExpNamedProperty).getBackslashEquivalent() = clazz + } + + /** + * Holds if `term` is a possessive quantifier, e.g. `a*+`. + */ + predicate isPossessive(RegExpQuantifier term) { term.isPossessive() } + + /** + * Holds if the regex that `term` is part of is used in a way that ignores any leading prefix of the input it's matched against. + */ + predicate matchesAnyPrefix(RegExpTerm term) { not term.getRegex().matchesFullString() } + + /** + * Holds if the regex that `term` is part of is used in a way that ignores any trailing suffix of the input it's matched against. + */ + predicate matchesAnySuffix(RegExpTerm term) { not term.getRegex().matchesFullString() } + + /** + * Holds if the regular expression should not be considered. + * + * We make the pragmatic performance optimization to ignore regular expressions in files + * that do not belong to the project code (such as installed dependencies). + */ + predicate isExcluded(RegExpParent parent) { + not exists(parent.getRegex().getLocation().getFile().getRelativePath()) + or + // Regexes with many occurrences of ".*" may cause the polynomial ReDoS computation to explode, so + // we explicitly exclude these. + strictcount(int i | exists(parent.getRegex().getText().regexpFind("\\.\\*", i, _)) | i) > 10 + } + + /** + * Holds if `root` has the `i` flag for case-insensitive matching. + */ + predicate isIgnoreCase(RegExpTerm root) { + root.isRootTerm() and + root.getLiteral().isIgnoreCase() + } + + /** + * Gets the flags for `root`, or the empty string if `root` has no flags. + */ + deprecated string getFlags(RegExpTerm root) { + root.isRootTerm() and + result = root.getLiteral().getFlags() + } + + /** + * Holds if `root` has the `s` flag for multi-line matching. + */ + predicate isDotAll(RegExpTerm root) { + root.isRootTerm() and + root.getLiteral().isDotAll() + } } - -/** Gets the parse tree resulting from parsing `re`, if such has been constructed. */ -RegExpTerm getParsedRegExp(StringLiteral re) { result.getRegex() = re and result.isRootTerm() } diff --git a/java/ql/lib/semmle/code/java/security/OverlyLargeRangeQuery.qll b/java/ql/lib/semmle/code/java/security/OverlyLargeRangeQuery.qll index 65e662f0bc5..06b538d4a63 100644 --- a/java/ql/lib/semmle/code/java/security/OverlyLargeRangeQuery.qll +++ b/java/ql/lib/semmle/code/java/security/OverlyLargeRangeQuery.qll @@ -2,288 +2,7 @@ * Classes and predicates for working with suspicious character ranges. */ -// We don't need the NFA utils, just the regexp tree. -// but the below is a nice shared library that exposes the API we need. -import regexp.NfaUtils - -/** - * Gets a rank for `range` that is unique for ranges in the same file. - * Prioritizes ranges that match more characters. - */ -int rankRange(RegExpCharacterRange range) { - range = - rank[result](RegExpCharacterRange r, Location l, int low, int high | - r.getLocation() = l and - isRange(r, low, high) - | - r order by (high - low) desc, l.getStartLine(), l.getStartColumn() - ) -} - -/** Holds if `range` spans from the unicode code points `low` to `high` (both inclusive). */ -predicate isRange(RegExpCharacterRange range, int low, int high) { - exists(string lowc, string highc | - range.isRange(lowc, highc) and - low.toUnicode() = lowc and - high.toUnicode() = highc - ) -} - -/** Holds if `char` is an alpha-numeric character. */ -predicate isAlphanumeric(string char) { - // written like this to avoid having a bindingset for the predicate - char = [[48 .. 57], [65 .. 90], [97 .. 122]].toUnicode() // 0-9, A-Z, a-z -} - -/** - * Holds if the given ranges are from the same character class - * and there exists at least one character matched by both ranges. - */ -predicate overlap(RegExpCharacterRange a, RegExpCharacterRange b) { - exists(RegExpCharacterClass clz | - a = clz.getAChild() and - b = clz.getAChild() and - a != b - | - exists(int alow, int ahigh, int blow, int bhigh | - isRange(a, alow, ahigh) and - isRange(b, blow, bhigh) and - alow <= bhigh and - blow <= ahigh - ) - ) -} - -/** - * Holds if `range` overlaps with the char class `escape` from the same character class. - */ -predicate overlapsWithCharEscape(RegExpCharacterRange range, RegExpCharacterClassEscape escape) { - exists(RegExpCharacterClass clz, string low, string high | - range = clz.getAChild() and - escape = clz.getAChild() and - range.isRange(low, high) - | - escape.getValue() = "w" and - getInRange(low, high).regexpMatch("\\w") - or - escape.getValue() = "d" and - getInRange(low, high).regexpMatch("\\d") - or - escape.getValue() = "s" and - getInRange(low, high).regexpMatch("\\s") - ) -} - -/** Gets the unicode code point for a `char`. */ -bindingset[char] -int toCodePoint(string char) { result.toUnicode() = char } - -/** A character range that appears to be overly wide. */ -class OverlyWideRange extends RegExpCharacterRange { - OverlyWideRange() { - exists(int low, int high, int numChars | - isRange(this, low, high) and - numChars = (1 + high - low) and - this.getRootTerm().isUsedAsRegExp() and - numChars >= 10 - | - // across the Z-a range (which includes backticks) - toCodePoint("Z") >= low and - toCodePoint("a") <= high - or - // across the 9-A range (which includes e.g. ; and ?) - toCodePoint("9") >= low and - toCodePoint("A") <= high - or - // a non-alphanumeric char as part of the range boundaries - exists(int bound | bound = [low, high] | not isAlphanumeric(bound.toUnicode())) and - // while still being ascii - low < 128 and - high < 128 - ) and - // allowlist for known ranges - not this = allowedWideRanges() - } - - /** Gets a string representation of a character class that matches the same chars as this range. */ - string printEquivalent() { result = RangePrinter::printEquivalentCharClass(this) } -} - -/** Gets a range that should not be reported as an overly wide range. */ -RegExpCharacterRange allowedWideRanges() { - // ~ is the last printable ASCII character, it's used right in various wide ranges. - result.isRange(_, "~") - or - // the same with " " and "!". " " is the first printable character, and "!" is the first non-white-space printable character. - result.isRange([" ", "!"], _) - or - // the `[@-_]` range is intentional - result.isRange("@", "_") - or - // starting from the zero byte is a good indication that it's purposely matching a large range. - result.isRange(0.toUnicode(), _) -} - -/** Gets a char between (and including) `low` and `high`. */ -bindingset[low, high] -private string getInRange(string low, string high) { - result = [toCodePoint(low) .. toCodePoint(high)].toUnicode() -} - -/** A module computing an equivalent character class for an overly wide range. */ -module RangePrinter { - bindingset[char] - bindingset[result] - private string next(string char) { - exists(int prev, int next | - prev.toUnicode() = char and - next.toUnicode() = result and - next = prev + 1 - ) - } - - /** Gets the points where the parts of the pretty printed range should be cut off. */ - private string cutoffs() { result = ["A", "Z", "a", "z", "0", "9"] } - - /** Gets the char to use in the low end of a range for a given `cut` */ - private string lowCut(string cut) { - cut = ["A", "a", "0"] and - result = cut - or - cut = ["Z", "z", "9"] and - result = next(cut) - } - - /** Gets the char to use in the high end of a range for a given `cut` */ - private string highCut(string cut) { - cut = ["Z", "z", "9"] and - result = cut - or - cut = ["A", "a", "0"] and - next(result) = cut - } - - /** Gets the cutoff char used for a given `part` of a range when pretty-printing it. */ - private string cutoff(OverlyWideRange range, int part) { - exists(int low, int high | isRange(range, low, high) | - result = - rank[part + 1](string cut | - cut = cutoffs() and low < toCodePoint(cut) and toCodePoint(cut) < high - | - cut order by toCodePoint(cut) - ) - ) - } - - /** Gets the number of parts we should print for a given `range`. */ - private int parts(OverlyWideRange range) { result = 1 + count(cutoff(range, _)) } - - /** Holds if the given part of a range should span from `low` to `high`. */ - private predicate part(OverlyWideRange range, int part, string low, string high) { - // first part. - part = 0 and - ( - range.isRange(low, high) and - parts(range) = 1 - or - parts(range) >= 2 and - range.isRange(low, _) and - high = highCut(cutoff(range, part)) - ) - or - // middle - part >= 1 and - part < parts(range) - 1 and - low = lowCut(cutoff(range, part - 1)) and - high = highCut(cutoff(range, part)) - or - // last. - part = parts(range) - 1 and - low = lowCut(cutoff(range, part - 1)) and - range.isRange(_, high) - } - - /** Gets an escaped `char` for use in a character class. */ - bindingset[char] - private string escape(string char) { - exists(string reg | reg = "(\\[|\\]|\\\\|-|/)" | - if char.regexpMatch(reg) then result = "\\" + char else result = char - ) - } - - /** Gets a part of the equivalent range. */ - private string printEquivalentCharClass(OverlyWideRange range, int part) { - exists(string low, string high | part(range, part, low, high) | - if - isAlphanumeric(low) and - isAlphanumeric(high) - then result = low + "-" + high - else - result = - strictconcat(string char | char = getInRange(low, high) | escape(char) order by char) - ) - } - - /** Gets the entire pretty printed equivalent range. */ - string printEquivalentCharClass(OverlyWideRange range) { - result = - strictconcat(string r, int part | - r = "[" and part = -1 and exists(range) - or - r = printEquivalentCharClass(range, part) - or - r = "]" and part = parts(range) - | - r order by part - ) - } -} - -/** Gets a char range that is overly large because of `reason`. */ -RegExpCharacterRange getABadRange(string reason, int priority) { - result instanceof OverlyWideRange and - priority = 0 and - exists(string equiv | equiv = result.(OverlyWideRange).printEquivalent() | - if equiv.length() <= 50 - then reason = "is equivalent to " + equiv - else reason = "is equivalent to " + equiv.substring(0, 50) + "..." - ) - or - priority = 1 and - exists(RegExpCharacterRange other | - reason = "overlaps with " + other + " in the same character class" and - rankRange(result) < rankRange(other) and - overlap(result, other) - ) - or - priority = 2 and - exists(RegExpCharacterClassEscape escape | - reason = "overlaps with " + escape + " in the same character class" and - overlapsWithCharEscape(result, escape) - ) - or - reason = "is empty" and - priority = 3 and - exists(int low, int high | - isRange(result, low, high) and - low > high - ) -} - -/** Holds if `range` matches suspiciously many characters. */ -predicate problem(RegExpCharacterRange range, string reason) { - reason = - strictconcat(string m, int priority | - range = getABadRange(m, priority) - | - m, ", and " order by priority desc - ) and - // specifying a range using an escape is usually OK. - not range.getAChild() instanceof RegExpEscape and - // Unicode escapes in strings are interpreted before it turns into a regexp, - // so e.g. [\u0001-\uFFFF] will just turn up as a range between two constants. - // We therefore exclude these ranges. - range.getRootTerm().getParent() instanceof RegExpLiteral and - // is used as regexp (mostly for JS where regular expressions are parsed eagerly) - range.getRootTerm().isUsedAsRegExp() -} +private import semmle.code.java.regex.RegexTreeView::RegexTreeView as TreeView +// OverlyLargeRangeQuery should be used directly from the shared pack, and not from this file. +deprecated import codeql.regex.OverlyLargeRangeQuery::Make as Dep +import Dep diff --git a/java/ql/lib/semmle/code/java/security/regexp/ExponentialBackTracking.qll b/java/ql/lib/semmle/code/java/security/regexp/ExponentialBackTracking.qll index 4a608890249..d0a08dc88bf 100644 --- a/java/ql/lib/semmle/code/java/security/regexp/ExponentialBackTracking.qll +++ b/java/ql/lib/semmle/code/java/security/regexp/ExponentialBackTracking.qll @@ -62,284 +62,7 @@ * a suffix `x` (possible empty) that is most likely __not__ accepted. */ -import NfaUtils - -/** - * Holds if state `s` might be inside a backtracking repetition. - */ -pragma[noinline] -private predicate stateInsideBacktracking(State s) { - s.getRepr().getParent*() instanceof MaybeBacktrackingRepetition -} - -/** - * A infinitely repeating quantifier that might backtrack. - */ -private class MaybeBacktrackingRepetition extends InfiniteRepetitionQuantifier { - MaybeBacktrackingRepetition() { - exists(RegExpTerm child | - child instanceof RegExpAlt or - child instanceof RegExpQuantifier - | - child.getParent+() = this - ) - } -} - -/** - * A state in the product automaton. - */ -private newtype TStatePair = - /** - * We lazily only construct those states that we are actually - * going to need: `(q, q)` for every fork state `q`, and any - * pair of states that can be reached from a pair that we have - * already constructed. To cut down on the number of states, - * we only represent states `(q1, q2)` where `q1` is lexicographically - * no bigger than `q2`. - * - * States are only constructed if both states in the pair are - * inside a repetition that might backtrack. - */ - MkStatePair(State q1, State q2) { - isFork(q1, _, _, _, _) and q2 = q1 - or - (step(_, _, _, q1, q2) or step(_, _, _, q2, q1)) and - rankState(q1) <= rankState(q2) - } - -/** - * Gets a unique number for a `state`. - * Is used to create an ordering of states, where states with the same `toString()` will be ordered differently. - */ -private int rankState(State state) { - state = - rank[result](State s, Location l | - stateInsideBacktracking(s) and - l = s.getRepr().getLocation() - | - s order by l.getStartLine(), l.getStartColumn(), s.toString() - ) -} - -/** - * A state in the product automaton. - */ -private class StatePair extends TStatePair { - State q1; - State q2; - - StatePair() { this = MkStatePair(q1, q2) } - - /** Gets a textual representation of this element. */ - string toString() { result = "(" + q1 + ", " + q2 + ")" } - - /** Gets the first component of the state pair. */ - State getLeft() { result = q1 } - - /** Gets the second component of the state pair. */ - State getRight() { result = q2 } -} - -/** - * Holds for `(fork, fork)` state pairs when `isFork(fork, _, _, _, _)` holds. - * - * Used in `statePairDistToFork` - */ -private predicate isStatePairFork(StatePair p) { - exists(State fork | p = MkStatePair(fork, fork) and isFork(fork, _, _, _, _)) -} - -/** - * Holds if there are transitions from the components of `q` to the corresponding - * components of `r`. - * - * Used in `statePairDistToFork` - */ -private predicate reverseStep(StatePair r, StatePair q) { step(q, _, _, r) } - -/** - * Gets the minimum length of a path from `q` to `r` in the - * product automaton. - */ -private int statePairDistToFork(StatePair q, StatePair r) = - shortestDistances(isStatePairFork/1, reverseStep/2)(r, q, result) - -/** - * Holds if there are transitions from `q` to `r1` and from `q` to `r2` - * labelled with `s1` and `s2`, respectively, where `s1` and `s2` do not - * trivially have an empty intersection. - * - * This predicate only holds for states associated with regular expressions - * that have at least one repetition quantifier in them (otherwise the - * expression cannot be vulnerable to ReDoS attacks anyway). - */ -pragma[noopt] -private predicate isFork(State q, InputSymbol s1, InputSymbol s2, State r1, State r2) { - stateInsideBacktracking(q) and - exists(State q1, State q2 | - q1 = epsilonSucc*(q) and - delta(q1, s1, r1) and - q2 = epsilonSucc*(q) and - delta(q2, s2, r2) and - // Use pragma[noopt] to prevent intersect(s1,s2) from being the starting point of the join. - // From (s1,s2) it would find a huge number of intermediate state pairs (q1,q2) originating from different literals, - // and discover at the end that no `q` can reach both `q1` and `q2` by epsilon transitions. - exists(intersect(s1, s2)) - | - s1 != s2 - or - r1 != r2 - or - r1 = r2 and q1 != q2 - or - // If q can reach itself by epsilon transitions, then there are two distinct paths to the q1/q2 state: - // one that uses the loop and one that doesn't. The engine will separately attempt to match with each path, - // despite ending in the same state. The "fork" thus arises from the choice of whether to use the loop or not. - // To avoid every state in the loop becoming a fork state, - // we arbitrarily pick the InfiniteRepetitionQuantifier state as the canonical fork state for the loop - // (every epsilon-loop must contain such a state). - // - // We additionally require that the there exists another InfiniteRepetitionQuantifier `mid` on the path from `q` to itself. - // This is done to avoid flagging regular expressions such as `/(a?)*b/` - that only has polynomial runtime, and is detected by `js/polynomial-redos`. - // The below code is therefore a heuristic, that only flags regular expressions such as `/(a*)*b/`, - // and does not flag regular expressions such as `/(a?b?)c/`, but the latter pattern is not used frequently. - r1 = r2 and - q1 = q2 and - epsilonSucc+(q) = q and - exists(RegExpTerm term | term = q.getRepr() | term instanceof InfiniteRepetitionQuantifier) and - // One of the mid states is an infinite quantifier itself - exists(State mid, RegExpTerm term | - mid = epsilonSucc+(q) and - term = mid.getRepr() and - term instanceof InfiniteRepetitionQuantifier and - q = epsilonSucc+(mid) and - not mid = q - ) - ) and - stateInsideBacktracking(r1) and - stateInsideBacktracking(r2) -} - -/** - * Gets the state pair `(q1, q2)` or `(q2, q1)`; note that only - * one or the other is defined. - */ -private StatePair mkStatePair(State q1, State q2) { - result = MkStatePair(q1, q2) or result = MkStatePair(q2, q1) -} - -/** - * Holds if there are transitions from the components of `q` to the corresponding - * components of `r` labelled with `s1` and `s2`, respectively. - */ -private predicate step(StatePair q, InputSymbol s1, InputSymbol s2, StatePair r) { - exists(State r1, State r2 | step(q, s1, s2, r1, r2) and r = mkStatePair(r1, r2)) -} - -/** - * Holds if there are transitions from the components of `q` to `r1` and `r2` - * labelled with `s1` and `s2`, respectively. - * - * We only consider transitions where the resulting states `(r1, r2)` are both - * inside a repetition that might backtrack. - */ -pragma[noopt] -private predicate step(StatePair q, InputSymbol s1, InputSymbol s2, State r1, State r2) { - exists(State q1, State q2 | q.getLeft() = q1 and q.getRight() = q2 | - deltaClosed(q1, s1, r1) and - deltaClosed(q2, s2, r2) and - // use noopt to force the join on `intersect` to happen last. - exists(intersect(s1, s2)) - ) and - stateInsideBacktracking(r1) and - stateInsideBacktracking(r2) -} - -private newtype TTrace = - Nil() or - Step(InputSymbol s1, InputSymbol s2, TTrace t) { isReachableFromFork(_, _, s1, s2, t, _) } - -/** - * A list of pairs of input symbols that describe a path in the product automaton - * starting from some fork state. - */ -private class Trace extends TTrace { - /** Gets a textual representation of this element. */ - string toString() { - this = Nil() and result = "Nil()" - or - exists(InputSymbol s1, InputSymbol s2, Trace t | this = Step(s1, s2, t) | - result = "Step(" + s1 + ", " + s2 + ", " + t + ")" - ) - } -} - -/** - * Holds if `r` is reachable from `(fork, fork)` under input `w`, and there is - * a path from `r` back to `(fork, fork)` with `rem` steps. - */ -private predicate isReachableFromFork(State fork, StatePair r, Trace w, int rem) { - exists(InputSymbol s1, InputSymbol s2, Trace v | - isReachableFromFork(fork, r, s1, s2, v, rem) and - w = Step(s1, s2, v) - ) -} - -private predicate isReachableFromFork( - State fork, StatePair r, InputSymbol s1, InputSymbol s2, Trace v, int rem -) { - // base case - exists(State q1, State q2 | - isFork(fork, s1, s2, q1, q2) and - r = MkStatePair(q1, q2) and - v = Nil() and - rem = statePairDistToFork(r, MkStatePair(fork, fork)) - ) - or - // recursive case - exists(StatePair p | - isReachableFromFork(fork, p, v, rem + 1) and - step(p, s1, s2, r) and - rem = statePairDistToFork(r, MkStatePair(fork, fork)) - ) -} - -/** - * Gets a state in the product automaton from which `(fork, fork)` is - * reachable in zero or more epsilon transitions. - */ -private StatePair getAForkPair(State fork) { - isFork(fork, _, _, _, _) and - result = MkStatePair(epsilonPred*(fork), epsilonPred*(fork)) -} - -/** An implementation of a chain containing chars for use by `Concretizer`. */ -private module CharTreeImpl implements CharTree { - class CharNode = Trace; - - CharNode getPrev(CharNode t) { t = Step(_, _, result) } - - /** Holds if `n` is a trace that is used by `concretize` in `isPumpable`. */ - predicate isARelevantEnd(CharNode n) { - exists(State f | isReachableFromFork(f, getAForkPair(f), n, _)) - } - - string getChar(CharNode t) { - exists(InputSymbol s1, InputSymbol s2 | t = Step(s1, s2, _) | result = intersect(s1, s2)) - } -} - -/** - * Holds if `fork` is a pumpable fork with word `w`. - */ -private predicate isPumpable(State fork, string w) { - exists(StatePair q, Trace t | - isReachableFromFork(fork, q, t, _) and - q = getAForkPair(fork) and - w = Concretizer::concretize(t) - ) -} - -/** Holds if `state` has exponential ReDoS */ -predicate hasReDoSResult = ReDoSPruning::hasReDoSResult/4; +private import semmle.code.java.regex.RegexTreeView::RegexTreeView as TreeView +// ExponentialBackTracking should be used directly from the shared pack, and not from this file. +deprecated private import codeql.regex.nfa.ExponentialBackTracking::Make as Dep +import Dep diff --git a/java/ql/lib/semmle/code/java/security/regexp/NfaUtils.qll b/java/ql/lib/semmle/code/java/security/regexp/NfaUtils.qll index 5ff0cb6a39e..3b69ecc7120 100644 --- a/java/ql/lib/semmle/code/java/security/regexp/NfaUtils.qll +++ b/java/ql/lib/semmle/code/java/security/regexp/NfaUtils.qll @@ -7,1332 +7,7 @@ * other queries that benefit from reasoning about NFAs. */ -import NfaUtilsSpecific - -/** - * Gets the char after `c` (from a simplified ASCII table). - */ -private string nextChar(string c) { exists(int code | code = ascii(c) | code + 1 = ascii(result)) } - -/** - * Gets an approximation for the ASCII code for `char`. - * Only the easily printable chars are included (so no newline, tab, null, etc). - */ -private int ascii(string char) { - char = - rank[result](string c | - c = - "! \"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" - .charAt(_) - ) -} - -/** - * Holds if `t` matches at least an epsilon symbol. - * - * That is, this term does not restrict the language of the enclosing regular expression. - * - * This is implemented as an under-approximation, and this predicate does not hold for sub-patterns in particular. - */ -predicate matchesEpsilon(RegExpTerm t) { - t instanceof RegExpStar - or - t instanceof RegExpOpt - or - t.(RegExpRange).getLowerBound() = 0 - or - exists(RegExpTerm child | - child = t.getAChild() and - matchesEpsilon(child) - | - t instanceof RegExpAlt or - t instanceof RegExpGroup or - t instanceof RegExpPlus or - t instanceof RegExpRange - ) - or - matchesEpsilon(t.(RegExpBackRef).getGroup()) - or - forex(RegExpTerm child | child = t.(RegExpSequence).getAChild() | matchesEpsilon(child)) -} - -/** - * A lookahead/lookbehind that matches the empty string. - */ -class EmptyPositiveSubPattern extends RegExpSubPattern { - EmptyPositiveSubPattern() { - ( - this instanceof RegExpPositiveLookahead - or - this instanceof RegExpPositiveLookbehind - ) and - matchesEpsilon(this.getOperand()) - } -} - -/** DEPRECATED: Use `EmptyPositiveSubPattern` instead. */ -deprecated class EmptyPositiveSubPatttern = EmptyPositiveSubPattern; - -/** - * A branch in a disjunction that is the root node in a literal, or a literal - * whose root node is not a disjunction. - */ -class RegExpRoot extends RegExpTerm { - RegExpRoot() { - exists(RegExpParent parent | - exists(RegExpAlt alt | - alt.isRootTerm() and - this = alt.getAChild() and - parent = alt.getParent() - ) - or - this.isRootTerm() and - not this instanceof RegExpAlt and - parent = this.getParent() - ) - } - - /** - * Holds if this root term is relevant to the ReDoS analysis. - */ - predicate isRelevant() { - // is actually used as a RegExp - this.isUsedAsRegExp() and - // not excluded for library specific reasons - not isExcluded(this.getRootTerm().getParent()) - } -} - -/** - * A constant in a regular expression that represents valid Unicode character(s). - */ -private class RegexpCharacterConstant extends RegExpConstant { - RegexpCharacterConstant() { this.isCharacter() } -} - -/** - * A regexp term that is relevant for this ReDoS analysis. - */ -class RelevantRegExpTerm extends RegExpTerm { - RelevantRegExpTerm() { getRoot(this).isRelevant() } -} - -/** - * Holds if `term` is the chosen canonical representative for all terms with string representation `str`. - * The string representation includes which flags are used with the regular expression. - * - * Using canonical representatives gives a huge performance boost when working with tuples containing multiple `InputSymbol`s. - * The number of `InputSymbol`s is decreased by 3 orders of magnitude or more in some larger benchmarks. - */ -private predicate isCanonicalTerm(RelevantRegExpTerm term, string str) { - term = - min(RelevantRegExpTerm t, Location loc, File file | - loc = t.getLocation() and - file = t.getFile() and - str = getCanonicalizationString(t) - | - t order by t.getFile().getRelativePath(), loc.getStartLine(), loc.getStartColumn() - ) -} - -/** - * Gets a string representation of `term` that is used for canonicalization. - */ -private string getCanonicalizationString(RelevantRegExpTerm term) { - exists(string ignoreCase | - (if RegExpFlags::isIgnoreCase(term.getRootTerm()) then ignoreCase = "i" else ignoreCase = "") and - result = term.getRawValue() + "|" + ignoreCase - ) -} - -/** - * An abstract input symbol, representing a set of concrete characters. - */ -private newtype TInputSymbol = - /** An input symbol corresponding to character `c`. */ - Char(string c) { - c = - any(RegexpCharacterConstant cc | - cc instanceof RelevantRegExpTerm and - not RegExpFlags::isIgnoreCase(cc.getRootTerm()) - ).getValue().charAt(_) - or - // normalize everything to lower case if the regexp is case insensitive - c = - any(RegexpCharacterConstant cc, string char | - cc instanceof RelevantRegExpTerm and - RegExpFlags::isIgnoreCase(cc.getRootTerm()) and - char = cc.getValue().charAt(_) - | - char.toLowerCase() - ) - } or - /** - * An input symbol representing all characters matched by - * a (non-universal) character class that has string representation `charClassString`. - */ - CharClass(string charClassString) { - exists(RelevantRegExpTerm recc | isCanonicalTerm(recc, charClassString) | - recc instanceof RegExpCharacterClass and - not recc.(RegExpCharacterClass).isUniversalClass() - or - isEscapeClass(recc, _) - ) - } or - /** An input symbol representing all characters matched by `.`. */ - Dot() or - /** An input symbol representing all characters. */ - Any() or - /** An epsilon transition in the automaton. */ - Epsilon() - -/** - * Gets the the CharClass corresponding to the canonical representative `term`. - */ -private CharClass getCharClassForCanonicalTerm(RegExpTerm term) { - exists(string str | isCanonicalTerm(term, str) | result = CharClass(str)) -} - -/** - * Gets a char class that represents `term`, even when `term` is not the canonical representative. - */ -CharacterClass getCanonicalCharClass(RegExpTerm term) { - exists(string str | str = getCanonicalizationString(term) and result = CharClass(str)) -} - -/** - * Holds if `a` and `b` are input symbols from the same regexp. - */ -private predicate sharesRoot(InputSymbol a, InputSymbol b) { - exists(RegExpRoot root | - belongsTo(a, root) and - belongsTo(b, root) - ) -} - -/** - * Holds if the `a` is an input symbol from a regexp that has root `root`. - */ -private predicate belongsTo(InputSymbol a, RegExpRoot root) { - exists(State s | getRoot(s.getRepr()) = root | - delta(s, a, _) - or - delta(_, a, s) - ) -} - -/** - * An abstract input symbol, representing a set of concrete characters. - */ -class InputSymbol extends TInputSymbol { - InputSymbol() { not this instanceof Epsilon } - - /** - * Gets a string representation of this input symbol. - */ - string toString() { - this = Char(result) - or - this = CharClass(result) - or - this = Dot() and result = "." - or - this = Any() and result = "[^]" - } -} - -/** - * An abstract input symbol that represents a character class. - */ -abstract class CharacterClass extends InputSymbol { - /** - * Gets a character that is relevant for intersection-tests involving this - * character class. - * - * Specifically, this is any of the characters mentioned explicitly in the - * character class, offset by one if it is inverted. For character class escapes, - * the result is as if the class had been written out as a series of intervals. - * - * This set is large enough to ensure that for any two intersecting character - * classes, one contains a relevant character from the other. - */ - abstract string getARelevantChar(); - - /** - * Holds if this character class matches `char`. - */ - bindingset[char] - abstract predicate matches(string char); - - /** - * Gets a character matched by this character class. - */ - string choose() { result = this.getARelevantChar() and this.matches(result) } -} - -/** - * Provides implementations for `CharacterClass`. - */ -private module CharacterClasses { - /** - * Holds if the character class `cc` has a child (constant or range) that matches `char`. - */ - pragma[noinline] - predicate hasChildThatMatches(RegExpCharacterClass cc, string char) { - if RegExpFlags::isIgnoreCase(cc.getRootTerm()) - then - // normalize everything to lower case if the regexp is case insensitive - exists(string c | hasChildThatMatchesIgnoringCasingFlags(cc, c) | char = c.toLowerCase()) - else hasChildThatMatchesIgnoringCasingFlags(cc, char) - } - - /** - * Holds if the character class `cc` has a child (constant or range) that matches `char`. - * Ignores whether the character class is inside a regular expression that has the ignore case flag. - */ - pragma[noinline] - predicate hasChildThatMatchesIgnoringCasingFlags(RegExpCharacterClass cc, string char) { - exists(getCharClassForCanonicalTerm(cc)) and - exists(RegExpTerm child | child = cc.getAChild() | - char = child.(RegexpCharacterConstant).getValue() - or - rangeMatchesOnLetterOrDigits(child, char) - or - not rangeMatchesOnLetterOrDigits(child, _) and - char = getARelevantChar() and - exists(string lo, string hi | child.(RegExpCharacterRange).isRange(lo, hi) | - lo <= char and - char <= hi - ) - or - exists(string charClass | isEscapeClass(child, charClass) | - charClass.toLowerCase() = charClass and - classEscapeMatches(charClass, char) - or - char = getARelevantChar() and - charClass.toUpperCase() = charClass and - not classEscapeMatches(charClass, char) - ) - ) - } - - /** - * Holds if `range` is a range on lower-case, upper-case, or digits, and matches `char`. - * This predicate is used to restrict the searchspace for ranges by only joining `getAnyPossiblyMatchedChar` - * on a few ranges. - */ - private predicate rangeMatchesOnLetterOrDigits(RegExpCharacterRange range, string char) { - exists(string lo, string hi | - range.isRange(lo, hi) and lo = lowercaseLetter() and hi = lowercaseLetter() - | - lo <= char and - char <= hi and - char = lowercaseLetter() - ) - or - exists(string lo, string hi | - range.isRange(lo, hi) and lo = upperCaseLetter() and hi = upperCaseLetter() - | - lo <= char and - char <= hi and - char = upperCaseLetter() - ) - or - exists(string lo, string hi | range.isRange(lo, hi) and lo = digit() and hi = digit() | - lo <= char and - char <= hi and - char = digit() - ) - } - - private string lowercaseLetter() { result = "abcdefghijklmnopqrstuvwxyz".charAt(_) } - - private string upperCaseLetter() { result = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".charAt(_) } - - private string digit() { result = [0 .. 9].toString() } - - /** - * Gets a char that could be matched by a regular expression. - * Includes all printable ascii chars, all constants mentioned in a regexp, and all chars matches by the regexp `/\s|\d|\w/`. - */ - string getARelevantChar() { - exists(ascii(result)) - or - exists(RegexpCharacterConstant c | result = c.getValue().charAt(_)) - or - classEscapeMatches(_, result) - } - - /** - * Gets a char that is mentioned in the character class `c`. - */ - private string getAMentionedChar(RegExpCharacterClass c) { - exists(RegExpTerm child | child = c.getAChild() | - result = child.(RegexpCharacterConstant).getValue() - or - child.(RegExpCharacterRange).isRange(result, _) - or - child.(RegExpCharacterRange).isRange(_, result) - or - exists(string charClass | isEscapeClass(child, charClass) | - result = min(string s | classEscapeMatches(charClass.toLowerCase(), s)) - or - result = max(string s | classEscapeMatches(charClass.toLowerCase(), s)) - ) - ) - } - - bindingset[char, cc] - private string caseNormalize(string char, RegExpTerm cc) { - if RegExpFlags::isIgnoreCase(cc.getRootTerm()) - then result = char.toLowerCase() - else result = char - } - - /** - * An implementation of `CharacterClass` for positive (non inverted) character classes. - */ - private class PositiveCharacterClass extends CharacterClass { - RegExpCharacterClass cc; - - PositiveCharacterClass() { this = getCharClassForCanonicalTerm(cc) and not cc.isInverted() } - - override string getARelevantChar() { result = caseNormalize(getAMentionedChar(cc), cc) } - - override predicate matches(string char) { hasChildThatMatches(cc, char) } - } - - /** - * An implementation of `CharacterClass` for inverted character classes. - */ - private class InvertedCharacterClass extends CharacterClass { - RegExpCharacterClass cc; - - InvertedCharacterClass() { this = getCharClassForCanonicalTerm(cc) and cc.isInverted() } - - override string getARelevantChar() { - result = nextChar(caseNormalize(getAMentionedChar(cc), cc)) or - nextChar(result) = caseNormalize(getAMentionedChar(cc), cc) - } - - bindingset[char] - override predicate matches(string char) { not hasChildThatMatches(cc, char) } - } - - /** - * Holds if the character class escape `clazz` (\d, \s, or \w) matches `char`. - */ - pragma[noinline] - private predicate classEscapeMatches(string clazz, string char) { - clazz = "d" and - char = "0123456789".charAt(_) - or - clazz = "s" and - char = [" ", "\t", "\r", "\n", 11.toUnicode(), 12.toUnicode()] // 11.toUnicode() = \v, 12.toUnicode() = \f - or - clazz = "w" and - char = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_".charAt(_) - } - - /** - * An implementation of `CharacterClass` for \d, \s, and \w. - */ - private class PositiveCharacterClassEscape extends CharacterClass { - string charClass; - RegExpTerm cc; - - PositiveCharacterClassEscape() { - isEscapeClass(cc, charClass) and - this = getCharClassForCanonicalTerm(cc) and - charClass = ["d", "s", "w"] - } - - override string getARelevantChar() { - charClass = "d" and - result = ["0", "9"] - or - charClass = "s" and - result = " " - or - charClass = "w" and - if RegExpFlags::isIgnoreCase(cc.getRootTerm()) - then result = ["a", "z", "_", "0", "9"] - else result = ["a", "Z", "_", "0", "9"] - } - - override predicate matches(string char) { classEscapeMatches(charClass, char) } - - override string choose() { - charClass = "d" and - result = "9" - or - charClass = "s" and - result = " " - or - charClass = "w" and - result = "a" - } - } - - /** - * An implementation of `CharacterClass` for \D, \S, and \W. - */ - private class NegativeCharacterClassEscape extends CharacterClass { - string charClass; - - NegativeCharacterClassEscape() { - exists(RegExpTerm cc | - isEscapeClass(cc, charClass) and - this = getCharClassForCanonicalTerm(cc) and - charClass = ["D", "S", "W"] - ) - } - - override string getARelevantChar() { - charClass = "D" and - result = ["a", "Z", "!"] - or - charClass = "S" and - result = ["a", "9", "!"] - or - charClass = "W" and - result = [" ", "!"] - } - - bindingset[char] - override predicate matches(string char) { - not classEscapeMatches(charClass.toLowerCase(), char) - } - } - - /** Gets a representative for all char classes that match the same chars as `c`. */ - CharacterClass normalize(CharacterClass c) { - exists(string normalization | - normalization = getNormalizationString(c) and - result = - min(CharacterClass cc, string raw | - getNormalizationString(cc) = normalization and cc = CharClass(raw) - | - cc order by raw - ) - ) - } - - /** Gets a string representing all the chars matched by `c` */ - private string getNormalizationString(CharacterClass c) { - (c instanceof PositiveCharacterClass or c instanceof PositiveCharacterClassEscape) and - result = concat(string char | c.matches(char) and char = CharacterClasses::getARelevantChar()) - or - (c instanceof InvertedCharacterClass or c instanceof NegativeCharacterClassEscape) and - // the string produced by the concat can not contain repeated chars - // so by starting the below with "nn" we can guarantee that - // it will not overlap with the above case. - // and a negative char class can never match the same chars as a positive one, so we don't miss any results from this. - result = - "nn:" + - concat(string char | not c.matches(char) and char = CharacterClasses::getARelevantChar()) - } -} - -private class EdgeLabel extends TInputSymbol { - string toString() { - this = Epsilon() and result = "" - or - exists(InputSymbol s | this = s and result = s.toString()) - } -} - -/** - * A RegExp term that acts like a plus. - * Either it's a RegExpPlus, or it is a range {1,X} where X is >= 30. - * 30 has been chosen as a threshold because for exponential blowup 2^30 is enough to get a decent DOS attack. - */ -private class EffectivelyPlus extends RegExpTerm { - EffectivelyPlus() { - this instanceof RegExpPlus - or - exists(RegExpRange range | - range.getLowerBound() = 1 and - (range.getUpperBound() >= 30 or not exists(range.getUpperBound())) - | - this = range - ) - } -} - -/** - * A RegExp term that acts like a star. - * Either it's a RegExpStar, or it is a range {0,X} where X is >= 30. - */ -private class EffectivelyStar extends RegExpTerm { - EffectivelyStar() { - this instanceof RegExpStar - or - exists(RegExpRange range | - range.getLowerBound() = 0 and - (range.getUpperBound() >= 30 or not exists(range.getUpperBound())) - | - this = range - ) - } -} - -/** - * A RegExp term that acts like a question mark. - * Either it's a RegExpQuestion, or it is a range {0,1}. - */ -private class EffectivelyQuestion extends RegExpTerm { - EffectivelyQuestion() { - this instanceof RegExpOpt - or - exists(RegExpRange range | range.getLowerBound() = 0 and range.getUpperBound() = 1 | - this = range - ) - } -} - -/** - * Gets the state before matching `t`. - */ -pragma[inline] -private State before(RegExpTerm t) { result = Match(t, 0) } - -/** - * Gets a state the NFA may be in after matching `t`. - */ -State after(RegExpTerm t) { - exists(RegExpAlt alt | t = alt.getAChild() | result = after(alt)) - or - exists(RegExpSequence seq, int i | t = seq.getChild(i) | - result = before(seq.getChild(i + 1)) - or - i + 1 = seq.getNumChild() and result = after(seq) - ) - or - exists(RegExpGroup grp | t = grp.getAChild() | result = after(grp)) - or - exists(EffectivelyStar star | t = star.getAChild() | - not isPossessive(star) and - result = before(star) - ) - or - exists(EffectivelyPlus plus | t = plus.getAChild() | - not isPossessive(plus) and - result = before(plus) - or - result = after(plus) - ) - or - exists(EffectivelyQuestion opt | t = opt.getAChild() | result = after(opt)) - or - exists(RegExpRoot root | t = root | - if matchesAnySuffix(root) then result = AcceptAnySuffix(root) else result = Accept(root) - ) -} - -/** - * Holds if the NFA has a transition from `q1` to `q2` labelled with `lbl`. - */ -predicate delta(State q1, EdgeLabel lbl, State q2) { - exists(RegexpCharacterConstant s, int i | - q1 = Match(s, i) and - ( - not RegExpFlags::isIgnoreCase(s.getRootTerm()) and - lbl = Char(s.getValue().charAt(i)) - or - // normalize everything to lower case if the regexp is case insensitive - RegExpFlags::isIgnoreCase(s.getRootTerm()) and - exists(string c | c = s.getValue().charAt(i) | lbl = Char(c.toLowerCase())) - ) and - ( - q2 = Match(s, i + 1) - or - s.getValue().length() = i + 1 and - q2 = after(s) - ) - ) - or - exists(RegExpDot dot | q1 = before(dot) and q2 = after(dot) | - if RegExpFlags::isDotAll(dot.getRootTerm()) then lbl = Any() else lbl = Dot() - ) - or - exists(RegExpCharacterClass cc | - cc.isUniversalClass() and q1 = before(cc) and lbl = Any() and q2 = after(cc) - or - q1 = before(cc) and - lbl = CharacterClasses::normalize(CharClass(getCanonicalizationString(cc))) and - q2 = after(cc) - ) - or - exists(RegExpTerm cc | isEscapeClass(cc, _) | - q1 = before(cc) and - lbl = CharacterClasses::normalize(CharClass(getCanonicalizationString(cc))) and - q2 = after(cc) - ) - or - exists(RegExpAlt alt | lbl = Epsilon() | q1 = before(alt) and q2 = before(alt.getAChild())) - or - exists(RegExpSequence seq | lbl = Epsilon() | q1 = before(seq) and q2 = before(seq.getChild(0))) - or - exists(RegExpGroup grp | lbl = Epsilon() | q1 = before(grp) and q2 = before(grp.getChild(0))) - or - exists(EffectivelyStar star | lbl = Epsilon() | - q1 = before(star) and q2 = before(star.getChild(0)) - or - q1 = before(star) and q2 = after(star) - ) - or - exists(EffectivelyPlus plus | lbl = Epsilon() | - q1 = before(plus) and q2 = before(plus.getChild(0)) - ) - or - exists(EffectivelyQuestion opt | lbl = Epsilon() | - q1 = before(opt) and q2 = before(opt.getChild(0)) - or - q1 = before(opt) and q2 = after(opt) - ) - or - exists(RegExpRoot root | q1 = AcceptAnySuffix(root) | - lbl = Any() and q2 = q1 - or - lbl = Epsilon() and q2 = Accept(root) - ) - or - exists(RegExpRoot root | q1 = Match(root, 0) | matchesAnyPrefix(root) and lbl = Any() and q2 = q1) - or - exists(RegExpDollar dollar | q1 = before(dollar) | - lbl = Epsilon() and q2 = Accept(getRoot(dollar)) - ) - or - exists(EmptyPositiveSubPattern empty | q1 = before(empty) | lbl = Epsilon() and q2 = after(empty)) -} - -/** - * Gets a state that `q` has an epsilon transition to. - */ -State epsilonSucc(State q) { delta(q, Epsilon(), result) } - -/** - * Gets a state that has an epsilon transition to `q`. - */ -State epsilonPred(State q) { q = epsilonSucc(result) } - -/** - * Holds if there is a state `q` that can be reached from `q1` - * along epsilon edges, such that there is a transition from - * `q` to `q2` that consumes symbol `s`. - */ -predicate deltaClosed(State q1, InputSymbol s, State q2) { delta(epsilonSucc*(q1), s, q2) } - -/** - * Gets the root containing the given term, that is, the root of the literal, - * or a branch of the root disjunction. - */ -RegExpRoot getRoot(RegExpTerm term) { - result = term or - result = getRoot(term.getParent()) -} - -/** - * A state in the NFA. - */ -newtype TState = - /** - * A state representing that the NFA is about to match a term. - * `i` is used to index into multi-char literals. - */ - Match(RelevantRegExpTerm t, int i) { - i = 0 - or - exists(t.(RegexpCharacterConstant).getValue().charAt(i)) - } or - /** - * An accept state, where exactly the given input string is accepted. - */ - Accept(RegExpRoot l) { l.isRelevant() } or - /** - * An accept state, where the given input string, or any string that has this - * string as a prefix, is accepted. - */ - AcceptAnySuffix(RegExpRoot l) { l.isRelevant() } - -/** - * Gets a state that is about to match the regular expression `t`. - */ -State mkMatch(RegExpTerm t) { result = Match(t, 0) } - -/** - * A state in the NFA corresponding to a regular expression. - * - * Each regular expression literal `l` has one accepting state - * `Accept(l)`, one state that accepts all suffixes `AcceptAnySuffix(l)`, - * and a state `Match(t, i)` for every subterm `t`, - * which represents the state of the NFA before starting to - * match `t`, or the `i`th character in `t` if `t` is a constant. - */ -class State extends TState { - RegExpTerm repr; - - State() { - this = Match(repr, _) or - this = Accept(repr) or - this = AcceptAnySuffix(repr) - } - - /** - * Gets a string representation for this state in a regular expression. - */ - string toString() { - exists(int i | this = Match(repr, i) | result = "Match(" + repr + "," + i + ")") - or - this instanceof Accept and - result = "Accept(" + repr + ")" - or - this instanceof AcceptAnySuffix and - result = "AcceptAny(" + repr + ")" - } - - /** - * Gets the location for this state. - */ - Location getLocation() { result = repr.getLocation() } - - /** - * Gets the term represented by this state. - */ - RegExpTerm getRepr() { result = repr } -} - -/** - * Gets the minimum char that is matched by both the character classes `c` and `d`. - */ -private string getMinOverlapBetweenCharacterClasses(CharacterClass c, CharacterClass d) { - result = min(getAOverlapBetweenCharacterClasses(c, d)) -} - -/** - * Gets a char that is matched by both the character classes `c` and `d`. - * And `c` and `d` is not the same character class. - */ -private string getAOverlapBetweenCharacterClasses(CharacterClass c, CharacterClass d) { - sharesRoot(c, d) and - result = [c.getARelevantChar(), d.getARelevantChar()] and - c.matches(result) and - d.matches(result) and - not c = d -} - -/** - * Gets a character that is represented by both `c` and `d`. - */ -string intersect(InputSymbol c, InputSymbol d) { - (sharesRoot(c, d) or [c, d] = Any()) and - ( - c = Char(result) and - d = getAnInputSymbolMatching(result) - or - result = getMinOverlapBetweenCharacterClasses(c, d) - or - result = c.(CharacterClass).choose() and - ( - d = c - or - d = Dot() and - not (result = "\n" or result = "\r") - or - d = Any() - ) - or - (c = Dot() or c = Any()) and - (d = Dot() or d = Any()) and - result = "a" - ) - or - result = intersect(d, c) -} - -/** - * Gets a symbol that matches `char`. - */ -bindingset[char] -InputSymbol getAnInputSymbolMatching(string char) { - result = Char(char) - or - result.(CharacterClass).matches(char) - or - result = Dot() and - not (char = "\n" or char = "\r") - or - result = Any() -} - -/** - * Holds if `state` is a start state. - */ -predicate isStartState(State state) { - state = mkMatch(any(RegExpRoot r)) - or - exists(RegExpCaret car | state = after(car)) -} - -/** - * Holds if `state` is a candidate for ReDoS with string `pump`. - */ -signature predicate isCandidateSig(State state, string pump); - -/** - * Holds if `state` is a candidate for ReDoS. - */ -signature predicate isCandidateSig(State state); - -/** - * Predicates for constructing a prefix string that leads to a given state. - */ -module PrefixConstruction { - /** - * Holds if `state` is the textually last start state for the regular expression. - */ - private predicate lastStartState(RelevantState state) { - exists(RegExpRoot root | - state = - max(RelevantState s, Location l | - isStartState(s) and - getRoot(s.getRepr()) = root and - l = s.getRepr().getLocation() - | - s - order by - l.getStartLine(), l.getStartColumn(), s.getRepr().toString(), l.getEndColumn(), - l.getEndLine() - ) - ) - } - - /** - * Holds if there exists any transition (Epsilon() or other) from `a` to `b`. - */ - private predicate existsTransition(State a, State b) { delta(a, _, b) } - - /** - * Gets the minimum number of transitions it takes to reach `state` from the `start` state. - */ - int prefixLength(State start, State state) = - shortestDistances(lastStartState/1, existsTransition/2)(start, state, result) - - /** - * Gets the minimum number of transitions it takes to reach `state` from the start state. - */ - private int lengthFromStart(State state) { result = prefixLength(_, state) } - - /** - * Gets a string for which the regular expression will reach `state`. - * - * Has at most one result for any given `state`. - * This predicate will not always have a result even if there is a ReDoS issue in - * the regular expression. - */ - string prefix(State state) { - lastStartState(state) and - result = "" - or - // the search stops past the last redos candidate state. - lengthFromStart(state) <= max(lengthFromStart(any(State s | isCandidate(s)))) and - exists(State prev | - // select a unique predecessor (by an arbitrary measure) - prev = - min(State s, Location loc | - lengthFromStart(s) = lengthFromStart(state) - 1 and - loc = s.getRepr().getLocation() and - delta(s, _, state) - | - s - order by - loc.getStartLine(), loc.getStartColumn(), loc.getEndLine(), loc.getEndColumn(), - s.getRepr().toString() - ) - | - // greedy search for the shortest prefix - result = prefix(prev) and delta(prev, Epsilon(), state) - or - not delta(prev, Epsilon(), state) and - result = prefix(prev) + getCanonicalEdgeChar(prev, state) - ) - } - - /** - * Gets a canonical char for which there exists a transition from `prev` to `next` in the NFA. - */ - private string getCanonicalEdgeChar(State prev, State next) { - result = - min(string c | delta(prev, any(InputSymbol symbol | c = intersect(Any(), symbol)), next)) - } - - /** A state within a regular expression that contains a candidate state. */ - class RelevantState instanceof State { - RelevantState() { - exists(State s | isCandidate(s) | getRoot(s.getRepr()) = getRoot(this.getRepr())) - } - - /** Gets a string representation for this state in a regular expression. */ - string toString() { result = State.super.toString() } - - /** Gets the term represented by this state. */ - RegExpTerm getRepr() { result = State.super.getRepr() } - } -} - -/** - * A module for pruning candidate ReDoS states. - * The candidates are specified by the `isCandidate` signature predicate. - * The candidates are checked for rejecting suffixes and deduplicated, - * and the resulting ReDoS states are read by the `hasReDoSResult` predicate. - */ -module ReDoSPruning { - /** - * Holds if repeating `pump` starting at `state` is a candidate for causing backtracking. - * No check whether a rejected suffix exists has been made. - */ - private predicate isReDoSCandidate(State state, string pump) { - isCandidate(state, pump) and - not state = acceptsAnySuffix() and // pruning early - these can never get stuck in a rejecting state. - ( - not isCandidate(epsilonSucc+(state), _) - or - epsilonSucc+(state) = state and - state = - max(State s, Location l | - s = epsilonSucc+(state) and - l = s.getRepr().getLocation() and - isCandidate(s, _) and - s.getRepr() instanceof InfiniteRepetitionQuantifier - | - s order by l.getStartLine(), l.getStartColumn(), l.getEndColumn(), l.getEndLine() - ) - ) - } - - /** Gets a state that can reach the `accept-any` state using only epsilon steps. */ - private State acceptsAnySuffix() { epsilonSucc*(result) = AcceptAnySuffix(_) } - - predicate isCandidateState(State s) { isReDoSCandidate(s, _) } - - import PrefixConstruction as Prefix - - class RelevantState = Prefix::RelevantState; - - /** - * Predicates for testing the presence of a rejecting suffix. - * - * These predicates are used to ensure that the all states reached from the fork - * by repeating `w` have a rejecting suffix. - * - * For example, a regexp like `/^(a+)+/` will accept any string as long the prefix is - * some number of `"a"`s, and it is therefore not possible to construct a rejecting suffix. - * - * A regexp like `/(a+)+$/` or `/(a+)+b/` trivially has a rejecting suffix, - * as the suffix "X" will cause both the regular expressions to be rejected. - * - * The string `w` is repeated any number of times because it needs to be - * infinitely repeatable for the attack to work. - * For the regular expression `/((ab)+)*abab/` the accepting state is not reachable from the fork - * using epsilon transitions. But any attempt at repeating `w` will end in a state that accepts all suffixes. - */ - private module SuffixConstruction { - /** - * Holds if all states reachable from `fork` by repeating `w` - * are likely rejectable by appending some suffix. - */ - predicate reachesOnlyRejectableSuffixes(State fork, string w) { - isReDoSCandidate(fork, w) and - forex(State next | next = process(fork, w, w.length() - 1) | isLikelyRejectable(next)) and - not getProcessPrevious(fork, _, w) = acceptsAnySuffix() // we stop `process(..)` early if we can, check here if it happened. - } - - /** - * Holds if there likely exists a suffix starting from `s` that leads to the regular expression being rejected. - * This predicate might find impossible suffixes when searching for suffixes of length > 1, which can cause FPs. - */ - pragma[noinline] - private predicate isLikelyRejectable(RelevantState s) { - // exists a reject edge with some char. - hasRejectEdge(s) - or - hasEdgeToLikelyRejectable(s) - or - // stopping here is rejection - isRejectState(s) - } - - /** - * Holds if `s` is not an accept state, and there is no epsilon transition to an accept state. - */ - predicate isRejectState(RelevantState s) { not epsilonSucc*(s) = Accept(_) } - - /** - * Holds if there is likely a non-empty suffix leading to rejection starting in `s`. - */ - pragma[noopt] - predicate hasEdgeToLikelyRejectable(RelevantState s) { - // all edges (at least one) with some char leads to another state that is rejectable. - // the `next` states might not share a common suffix, which can cause FPs. - exists(string char | char = hasEdgeToLikelyRejectableHelper(s) | - // noopt to force `hasEdgeToLikelyRejectableHelper` to be first in the join-order. - exists(State next | deltaClosedChar(s, char, next) | isLikelyRejectable(next)) and - forall(State next | deltaClosedChar(s, char, next) | isLikelyRejectable(next)) - ) - } - - /** - * Gets a char for there exists a transition away from `s`, - * and `s` has not been found to be rejectable by `hasRejectEdge` or `isRejectState`. - */ - pragma[noinline] - private string hasEdgeToLikelyRejectableHelper(RelevantState s) { - not hasRejectEdge(s) and - not isRejectState(s) and - deltaClosedChar(s, result, _) - } - - /** - * Holds if there is a state `next` that can be reached from `prev` - * along epsilon edges, such that there is a transition from - * `prev` to `next` that the character symbol `char`. - */ - predicate deltaClosedChar(RelevantState prev, string char, RelevantState next) { - deltaClosed(prev, getAnInputSymbolMatchingRelevant(char), next) - } - - pragma[noinline] - InputSymbol getAnInputSymbolMatchingRelevant(string char) { - char = relevant(_) and - result = getAnInputSymbolMatching(char) - } - - pragma[noinline] - RegExpRoot relevantRoot() { - exists(RegExpTerm term, State s | - s.getRepr() = term and isCandidateState(s) and result = term.getRootTerm() - ) - } - - /** - * Gets a char used for finding possible suffixes inside `root`. - */ - pragma[noinline] - private string relevant(RegExpRoot root) { - root = relevantRoot() and - ( - exists(ascii(result)) and exists(root) - or - exists(InputSymbol s | belongsTo(s, root) | result = intersect(s, _)) - or - // The characters from `hasSimpleRejectEdge`. Only `\n` is really needed (as `\n` is not in the `ascii` relation). - // The three chars must be kept in sync with `hasSimpleRejectEdge`. - result = ["|", "\n", "Z"] and exists(root) - ) - } - - /** - * Holds if there exists a `char` such that there is no edge from `s` labeled `char` in our NFA. - * The NFA does not model reject states, so the above is the same as saying there is a reject edge. - */ - private predicate hasRejectEdge(State s) { - hasSimpleRejectEdge(s) - or - not hasSimpleRejectEdge(s) and - exists(string char | char = relevant(getRoot(s.getRepr())) | not deltaClosedChar(s, char, _)) - } - - /** - * Holds if there is no edge from `s` labeled with "|", "\n", or "Z" in our NFA. - * This predicate is used as a cheap pre-processing to speed up `hasRejectEdge`. - */ - private predicate hasSimpleRejectEdge(State s) { - // The three chars were chosen arbitrarily. The three chars must be kept in sync with `relevant`. - exists(string char | char = ["|", "\n", "Z"] | not deltaClosedChar(s, char, _)) - } - - /** - * Gets a state that can be reached from pumpable `fork` consuming all - * chars in `w` any number of times followed by the first `i+1` characters of `w`. - */ - pragma[noopt] - private State process(State fork, string w, int i) { - exists(State prev | prev = getProcessPrevious(fork, i, w) | - not prev = acceptsAnySuffix() and // we stop `process(..)` early if we can. If the successor accepts any suffix, then we know it can never be rejected. - exists(string char, InputSymbol sym | - char = w.charAt(i) and - deltaClosed(prev, sym, result) and - // noopt to prevent joining `prev` with all possible `chars` that could transition away from `prev`. - // Instead only join with the set of `chars` where a relevant `InputSymbol` has already been found. - sym = getAProcessInputSymbol(char) - ) - ) - } - - /** - * Gets a state that can be reached from pumpable `fork` consuming all - * chars in `w` any number of times followed by the first `i` characters of `w`. - */ - private State getProcessPrevious(State fork, int i, string w) { - isReDoSCandidate(fork, w) and - ( - i = 0 and result = fork - or - result = process(fork, w, i - 1) - or - // repeat until fixpoint - i = 0 and - result = process(fork, w, w.length() - 1) - ) - } - - /** - * Gets an InputSymbol that matches `char`. - * The predicate is specialized to only have a result for the `char`s that are relevant for the `process` predicate. - */ - private InputSymbol getAProcessInputSymbol(string char) { - char = getAProcessChar() and - result = getAnInputSymbolMatching(char) - } - - /** - * Gets a `char` that occurs in a `pump` string. - */ - private string getAProcessChar() { result = any(string s | isReDoSCandidate(_, s)).charAt(_) } - } - - /** - * Holds if `term` may cause superlinear backtracking on strings containing many repetitions of `pump`. - * Gets the shortest string that causes superlinear backtracking. - */ - private predicate isReDoSAttackable(RegExpTerm term, string pump, State s) { - exists(int i, string c | s = Match(term, i) | - c = - min(string w | - isCandidate(s, w) and - SuffixConstruction::reachesOnlyRejectableSuffixes(s, w) - | - w order by w.length(), w - ) and - pump = escape(rotate(c, i)) - ) - } - - /** - * Holds if the state `s` (represented by the term `t`) can have backtracking with repetitions of `pump`. - * - * `prefixMsg` contains a friendly message for a prefix that reaches `s` (or `prefixMsg` is the empty string if the prefix is empty or if no prefix could be found). - */ - predicate hasReDoSResult(RegExpTerm t, string pump, State s, string prefixMsg) { - isReDoSAttackable(t, pump, s) and - ( - prefixMsg = "starting with '" + escape(Prefix::prefix(s)) + "' and " and - not Prefix::prefix(s) = "" - or - Prefix::prefix(s) = "" and prefixMsg = "" - or - not exists(Prefix::prefix(s)) and prefixMsg = "" - ) - } - - /** - * Gets the result of backslash-escaping newlines, carriage-returns and - * backslashes in `s`. - */ - bindingset[s] - private string escape(string s) { - result = - s.replaceAll("\\", "\\\\") - .replaceAll("\n", "\\n") - .replaceAll("\r", "\\r") - .replaceAll("\t", "\\t") - } - - /** - * Gets `str` with the last `i` characters moved to the front. - * - * We use this to adjust the pump string to match with the beginning of - * a RegExpTerm, so it doesn't start in the middle of a constant. - */ - bindingset[str, i] - private string rotate(string str, int i) { - result = str.suffix(str.length() - i) + str.prefix(str.length() - i) - } -} - -/** - * A module that describes a tree where each node has one or more associated characters, also known as a trie. - * The root node has no associated character. - * This module is a signature used in `Concretizer`. - */ -signature module CharTree { - /** A node in the tree. */ - class CharNode; - - /** Gets the previous node in the tree from `t`. */ - CharNode getPrev(CharNode t); - - /** - * Holds if `n` is at the end of a tree. I.e. a node that should have a result in the `Concretizer` module. - * Such a node can still have children. - */ - predicate isARelevantEnd(CharNode n); - - /** Gets a char associated with `t`. */ - string getChar(CharNode t); -} - -/** - * Implements an algorithm for computing all possible strings - * from following a tree of nodes (as described in `CharTree`). - * - * The string is build using one big concat, where all the chars are computed first. - * See `concretize`. - */ -module Concretizer { - private class Node = Impl::CharNode; - - private predicate getPrev = Impl::getPrev/1; - - private predicate isARelevantEnd = Impl::isARelevantEnd/1; - - private predicate getChar = Impl::getChar/1; - - /** Holds if `n` is on a path from the root to a leaf, and is therefore relevant for the results in `concretize`. */ - private predicate isRelevant(Node n) { - isARelevantEnd(n) - or - exists(Node succ | isRelevant(succ) | n = getPrev(succ)) - } - - /** Holds if `n` is a root with no predecessors. */ - private predicate isRoot(Node n) { not exists(getPrev(n)) } - - /** Gets the distance from a root to `n`. */ - private int nodeDepth(Node n) { - result = 0 and isRoot(n) - or - isRelevant(n) and - exists(Node prev | result = nodeDepth(prev) + 1 | prev = getPrev(n)) - } - - /** Gets an ancestor of `end`, where `end` is a node that should have a result in `concretize`. */ - private Node getAnAncestor(Node end) { isARelevantEnd(end) and result = getPrev*(end) } - - /** Gets the `i`th character on the path from the root to `n`. */ - pragma[noinline] - private string getPrefixChar(Node n, int i) { - exists(Node ancestor | - result = getChar(ancestor) and - ancestor = getAnAncestor(n) and - i = nodeDepth(ancestor) - ) - } - - /** Gets a string corresponding to `node`. */ - language[monotonicAggregates] - string concretize(Node n) { - result = strictconcat(int i | exists(getPrefixChar(n, i)) | getPrefixChar(n, i) order by i) - } -} +private import semmle.code.java.regex.RegexTreeView::RegexTreeView as TreeView +// NfaUtils should be used directly from the shared pack, and not from this file. +deprecated private import codeql.regex.nfa.NfaUtils::Make as Dep +import Dep diff --git a/java/ql/lib/semmle/code/java/security/regexp/NfaUtilsSpecific.qll b/java/ql/lib/semmle/code/java/security/regexp/NfaUtilsSpecific.qll deleted file mode 100644 index 742229eacca..00000000000 --- a/java/ql/lib/semmle/code/java/security/regexp/NfaUtilsSpecific.qll +++ /dev/null @@ -1,76 +0,0 @@ -/** - * This module should provide a class hierarchy corresponding to a parse tree of regular expressions. - * This is the interface to the shared ReDoS library. - */ - -private import java -import semmle.code.FileSystem -import semmle.code.java.regex.RegexTreeView - -/** - * Holds if `term` is an escape class representing e.g. `\d`. - * `clazz` is which character class it represents, e.g. "d" for `\d`. - */ -predicate isEscapeClass(RegExpTerm term, string clazz) { - term.(RegExpCharacterClassEscape).getValue() = clazz - or - term.(RegExpNamedProperty).getBackslashEquivalent() = clazz -} - -/** - * Holds if `term` is a possessive quantifier, e.g. `a*+`. - */ -predicate isPossessive(RegExpQuantifier term) { term.isPossessive() } - -/** - * Holds if the regex that `term` is part of is used in a way that ignores any leading prefix of the input it's matched against. - */ -predicate matchesAnyPrefix(RegExpTerm term) { not term.getRegex().matchesFullString() } - -/** - * Holds if the regex that `term` is part of is used in a way that ignores any trailing suffix of the input it's matched against. - */ -predicate matchesAnySuffix(RegExpTerm term) { not term.getRegex().matchesFullString() } - -/** - * Holds if the regular expression should not be considered. - * - * We make the pragmatic performance optimization to ignore regular expressions in files - * that do not belong to the project code (such as installed dependencies). - */ -predicate isExcluded(RegExpParent parent) { - not exists(parent.getRegex().getLocation().getFile().getRelativePath()) - or - // Regexes with many occurrences of ".*" may cause the polynomial ReDoS computation to explode, so - // we explicitly exclude these. - strictcount(int i | exists(parent.getRegex().getText().regexpFind("\\.\\*", i, _)) | i) > 10 -} - -/** - * A module containing predicates for determining which flags a regular expression have. - */ -module RegExpFlags { - /** - * Holds if `root` has the `i` flag for case-insensitive matching. - */ - predicate isIgnoreCase(RegExpTerm root) { - root.isRootTerm() and - root.getLiteral().isIgnoreCase() - } - - /** - * Gets the flags for `root`, or the empty string if `root` has no flags. - */ - deprecated string getFlags(RegExpTerm root) { - root.isRootTerm() and - result = root.getLiteral().getFlags() - } - - /** - * Holds if `root` has the `s` flag for multi-line matching. - */ - predicate isDotAll(RegExpTerm root) { - root.isRootTerm() and - root.getLiteral().isDotAll() - } -} diff --git a/java/ql/lib/semmle/code/java/security/regexp/PolynomialReDoSQuery.qll b/java/ql/lib/semmle/code/java/security/regexp/PolynomialReDoSQuery.qll index b0a8ff1a3c5..2a822ac69de 100644 --- a/java/ql/lib/semmle/code/java/security/regexp/PolynomialReDoSQuery.qll +++ b/java/ql/lib/semmle/code/java/security/regexp/PolynomialReDoSQuery.qll @@ -1,19 +1,19 @@ /** Definitions and configurations for the Polynomial ReDoS query */ -import semmle.code.java.security.regexp.SuperlinearBackTracking +private import semmle.code.java.regex.RegexTreeView::RegexTreeView as TreeView +import codeql.regex.nfa.SuperlinearBackTracking::Make as SuperlinearBackTracking import semmle.code.java.dataflow.DataFlow -import semmle.code.java.regex.RegexTreeView import semmle.code.java.regex.RegexFlowConfigs import semmle.code.java.dataflow.FlowSources /** A sink for polynomial redos queries, where a regex is matched. */ class PolynomialRedosSink extends DataFlow::Node { - RegExpLiteral reg; + TreeView::RegExpLiteral reg; PolynomialRedosSink() { regexMatchedAgainst(reg.getRegex(), this.asExpr()) } /** Gets the regex that is matched against this node. */ - RegExpTerm getRegExp() { result.getParent() = reg } + TreeView::RegExpTerm getRegExp() { result.getParent() = reg } } /** @@ -49,7 +49,8 @@ class PolynomialRedosConfig extends TaintTracking::Configuration { /** Holds if there is flow from `source` to `sink` that is matched against the regexp term `regexp` that is vulnerable to Polynomial ReDoS. */ predicate hasPolynomialReDoSResult( - DataFlow::PathNode source, DataFlow::PathNode sink, PolynomialBackTrackingTerm regexp + DataFlow::PathNode source, DataFlow::PathNode sink, + SuperlinearBackTracking::PolynomialBackTrackingTerm regexp ) { any(PolynomialRedosConfig config).hasFlowPath(source, sink) and regexp.getRootTerm() = sink.getNode().(PolynomialRedosSink).getRegExp() diff --git a/java/ql/lib/semmle/code/java/security/regexp/SuperlinearBackTracking.qll b/java/ql/lib/semmle/code/java/security/regexp/SuperlinearBackTracking.qll index 14a69dc0644..623b1540ef1 100644 --- a/java/ql/lib/semmle/code/java/security/regexp/SuperlinearBackTracking.qll +++ b/java/ql/lib/semmle/code/java/security/regexp/SuperlinearBackTracking.qll @@ -1,11 +1,4 @@ /** - * Provides classes for working with regular expressions that can - * perform backtracking in superlinear time. - */ - -import NfaUtils - -/* * This module implements the analysis described in the paper: * Valentin Wustholz, Oswaldo Olivo, Marijn J. H. Heule, and Isil Dillig: * Static Detection of DoS Vulnerabilities in @@ -42,377 +35,7 @@ import NfaUtils * It also doesn't find all transitions in the product automaton, which can cause false negatives. */ -/** - * Gets any root (start) state of a regular expression. - */ -private State getRootState() { result = mkMatch(any(RegExpRoot r)) } - -private newtype TStateTuple = - MkStateTuple(State q1, State q2, State q3) { - // starts at (pivot, pivot, succ) - isStartLoops(q1, q3) and q1 = q2 - or - step(_, _, _, _, q1, q2, q3) and FeasibleTuple::isFeasibleTuple(q1, q2, q3) - } - -/** - * A state in the product automaton. - * The product automaton contains 3-tuples of states. - * - * We lazily only construct those states that we are actually - * going to need. - * Either a start state `(pivot, pivot, succ)`, or a state - * where there exists a transition from an already existing state. - * - * The exponential variant of this query (`js/redos`) uses an optimization - * trick where `q1 <= q2`. This trick cannot be used here as the order - * of the elements matter. - */ -class StateTuple extends TStateTuple { - State q1; - State q2; - State q3; - - StateTuple() { this = MkStateTuple(q1, q2, q3) } - - /** - * Gest a string representation of this tuple. - */ - string toString() { result = "(" + q1 + ", " + q2 + ", " + q3 + ")" } - - /** - * Holds if this tuple is `(r1, r2, r3)`. - */ - pragma[noinline] - predicate isTuple(State r1, State r2, State r3) { r1 = q1 and r2 = q2 and r3 = q3 } -} - -/** - * A module for determining feasible tuples for the product automaton. - * - * The implementation is split into many predicates for performance reasons. - */ -private module FeasibleTuple { - /** - * Holds if the tuple `(r1, r2, r3)` might be on path from a start-state to an end-state in the product automaton. - */ - pragma[inline] - predicate isFeasibleTuple(State r1, State r2, State r3) { - // The first element is either inside a repetition (or the start state itself) - isRepetitionOrStart(r1) and - // The last element is inside a repetition - stateInsideRepetition(r3) and - // The states are reachable in the NFA in the order r1 -> r2 -> r3 - delta+(r1) = r2 and - delta+(r2) = r3 and - // The first element can reach a beginning (the "pivot" state in a `(pivot, succ)` pair). - canReachABeginning(r1) and - // The last element can reach a target (the "succ" state in a `(pivot, succ)` pair). - canReachATarget(r3) - } - - /** - * Holds if `s` is either inside a repetition, or is the start state (which is a repetition). - */ - pragma[noinline] - private predicate isRepetitionOrStart(State s) { stateInsideRepetition(s) or s = getRootState() } - - /** - * Holds if state `s` might be inside a backtracking repetition. - */ - pragma[noinline] - private predicate stateInsideRepetition(State s) { - s.getRepr().getParent*() instanceof InfiniteRepetitionQuantifier - } - - /** - * Holds if there exists a path in the NFA from `s` to a "pivot" state - * (from a `(pivot, succ)` pair that starts the search). - */ - pragma[noinline] - private predicate canReachABeginning(State s) { - delta+(s) = any(State pivot | isStartLoops(pivot, _)) - } - - /** - * Holds if there exists a path in the NFA from `s` to a "succ" state - * (from a `(pivot, succ)` pair that starts the search). - */ - pragma[noinline] - private predicate canReachATarget(State s) { delta+(s) = any(State succ | isStartLoops(_, succ)) } -} - -/** - * Holds if `pivot` and `succ` are a pair of loops that could be the beginning of a quadratic blowup. - * - * There is a slight implementation difference compared to the paper: this predicate requires that `pivot != succ`. - * The case where `pivot = succ` causes exponential backtracking and is handled by the `js/redos` query. - */ -predicate isStartLoops(State pivot, State succ) { - pivot != succ and - succ.getRepr() instanceof InfiniteRepetitionQuantifier and - delta+(pivot) = succ and - ( - pivot.getRepr() instanceof InfiniteRepetitionQuantifier - or - pivot = mkMatch(any(RegExpRoot root)) - ) -} - -/** - * Gets a state for which there exists a transition in the NFA from `s'. - */ -State delta(State s) { delta(s, _, result) } - -/** - * Holds if there are transitions from the components of `q` to the corresponding - * components of `r` labelled with `s1`, `s2`, and `s3`, respectively. - */ -pragma[noinline] -predicate step(StateTuple q, InputSymbol s1, InputSymbol s2, InputSymbol s3, StateTuple r) { - exists(State r1, State r2, State r3 | - step(q, s1, s2, s3, r1, r2, r3) and r = MkStateTuple(r1, r2, r3) - ) -} - -/** - * Holds if there are transitions from the components of `q` to `r1`, `r2`, and `r3 - * labelled with `s1`, `s2`, and `s3`, respectively. - */ -pragma[noopt] -predicate step( - StateTuple q, InputSymbol s1, InputSymbol s2, InputSymbol s3, State r1, State r2, State r3 -) { - exists(State q1, State q2, State q3 | q.isTuple(q1, q2, q3) | - deltaClosed(q1, s1, r1) and - deltaClosed(q2, s2, r2) and - deltaClosed(q3, s3, r3) and - // use noopt to force the join on `getAThreewayIntersect` to happen last. - exists(getAThreewayIntersect(s1, s2, s3)) - ) -} - -/** - * Gets a char that is matched by all the edges `s1`, `s2`, and `s3`. - * - * The result is not complete, and might miss some combination of edges that share some character. - */ -pragma[noinline] -string getAThreewayIntersect(InputSymbol s1, InputSymbol s2, InputSymbol s3) { - result = minAndMaxIntersect(s1, s2) and result = [intersect(s2, s3), intersect(s1, s3)] - or - result = minAndMaxIntersect(s1, s3) and result = [intersect(s2, s3), intersect(s1, s2)] - or - result = minAndMaxIntersect(s2, s3) and result = [intersect(s1, s2), intersect(s1, s3)] -} - -/** - * Gets the minimum and maximum characters that intersect between `a` and `b`. - * This predicate is used to limit the size of `getAThreewayIntersect`. - */ -pragma[noinline] -string minAndMaxIntersect(InputSymbol a, InputSymbol b) { - result = [min(intersect(a, b)), max(intersect(a, b))] -} - -private newtype TTrace = - Nil() or - Step(InputSymbol s1, InputSymbol s2, InputSymbol s3, TTrace t) { - isReachableFromStartTuple(_, _, t, s1, s2, s3, _, _) - } - -/** - * A list of tuples of input symbols that describe a path in the product automaton - * starting from some start state. - */ -class Trace extends TTrace { - /** - * Gets a string representation of this Trace that can be used for debug purposes. - */ - string toString() { - this = Nil() and result = "Nil()" - or - exists(InputSymbol s1, InputSymbol s2, InputSymbol s3, Trace t | this = Step(s1, s2, s3, t) | - result = "Step(" + s1 + ", " + s2 + ", " + s3 + ", " + t + ")" - ) - } -} - -/** - * Holds if there exists a transition from `r` to `q` in the product automaton. - * Notice that the arguments are flipped, and thus the direction is backwards. - */ -pragma[noinline] -predicate tupleDeltaBackwards(StateTuple q, StateTuple r) { step(r, _, _, _, q) } - -/** - * Holds if `tuple` is an end state in our search. - * That means there exists a pair of loops `(pivot, succ)` such that `tuple = (pivot, succ, succ)`. - */ -predicate isEndTuple(StateTuple tuple) { tuple = getAnEndTuple(_, _) } - -/** - * Gets the minimum length of a path from `r` to some an end state `end`. - * - * The implementation searches backwards from the end-tuple. - * This approach was chosen because it is way more efficient if the first predicate given to `shortestDistances` is small. - * The `end` argument must always be an end state. - */ -int distBackFromEnd(StateTuple r, StateTuple end) = - shortestDistances(isEndTuple/1, tupleDeltaBackwards/2)(end, r, result) - -/** - * Holds if there exists a pair of repetitions `(pivot, succ)` in the regular expression such that: - * `tuple` is reachable from `(pivot, pivot, succ)` in the product automaton, - * and there is a distance of `dist` from `tuple` to the nearest end-tuple `(pivot, succ, succ)`, - * and a path from a start-state to `tuple` follows the transitions in `trace`. - */ -private predicate isReachableFromStartTuple( - State pivot, State succ, StateTuple tuple, Trace trace, int dist -) { - exists(InputSymbol s1, InputSymbol s2, InputSymbol s3, Trace v | - isReachableFromStartTuple(pivot, succ, v, s1, s2, s3, tuple, dist) and - trace = Step(s1, s2, s3, v) - ) -} - -private predicate isReachableFromStartTuple( - State pivot, State succ, Trace trace, InputSymbol s1, InputSymbol s2, InputSymbol s3, - StateTuple tuple, int dist -) { - // base case. - exists(State q1, State q2, State q3 | - isStartLoops(pivot, succ) and - step(MkStateTuple(pivot, pivot, succ), s1, s2, s3, tuple) and - tuple = MkStateTuple(q1, q2, q3) and - trace = Nil() and - dist = distBackFromEnd(tuple, MkStateTuple(pivot, succ, succ)) - ) - or - // recursive case - exists(StateTuple p | - isReachableFromStartTuple(pivot, succ, p, trace, dist + 1) and - dist = distBackFromEnd(tuple, MkStateTuple(pivot, succ, succ)) and - step(p, s1, s2, s3, tuple) - ) -} - -/** - * Gets the tuple `(pivot, succ, succ)` from the product automaton. - */ -StateTuple getAnEndTuple(State pivot, State succ) { - isStartLoops(pivot, succ) and - result = MkStateTuple(pivot, succ, succ) -} - -/** An implementation of a chain containing chars for use by `Concretizer`. */ -private module CharTreeImpl implements CharTree { - class CharNode = Trace; - - CharNode getPrev(CharNode t) { t = Step(_, _, _, result) } - - /** Holds if `n` is used in `isPumpable`. */ - predicate isARelevantEnd(CharNode n) { - exists(State pivot, State succ | - isReachableFromStartTuple(pivot, succ, getAnEndTuple(pivot, succ), n, _) - ) - } - - string getChar(CharNode t) { - exists(InputSymbol s1, InputSymbol s2, InputSymbol s3 | t = Step(s1, s2, s3, _) | - result = getAThreewayIntersect(s1, s2, s3) - ) - } -} - -/** - * Holds if matching repetitions of `pump` can: - * 1) Transition from `pivot` back to `pivot`. - * 2) Transition from `pivot` to `succ`. - * 3) Transition from `succ` to `succ`. - * - * From theorem 3 in the paper linked in the top of this file we can therefore conclude that - * the regular expression has polynomial backtracking - if a rejecting suffix exists. - * - * This predicate is used by `SuperLinearReDoSConfiguration`, and the final results are - * available in the `hasReDoSResult` predicate. - */ -predicate isPumpable(State pivot, State succ, string pump) { - exists(StateTuple q, Trace t | - isReachableFromStartTuple(pivot, succ, q, t, _) and - q = getAnEndTuple(pivot, succ) and - pump = Concretizer::concretize(t) - ) -} - -/** - * Holds if states starting in `state` can have polynomial backtracking with the string `pump`. - */ -predicate isReDoSCandidate(State state, string pump) { isPumpable(_, state, pump) } - -/** - * Holds if repetitions of `pump` at `t` will cause polynomial backtracking. - */ -predicate polynomialReDoS(RegExpTerm t, string pump, string prefixMsg, RegExpTerm prev) { - exists(State s, State pivot | - ReDoSPruning::hasReDoSResult(t, pump, s, prefixMsg) and - isPumpable(pivot, s, _) and - prev = pivot.getRepr() - ) -} - -/** - * Gets a message for why `term` can cause polynomial backtracking. - */ -string getReasonString(RegExpTerm term, string pump, string prefixMsg, RegExpTerm prev) { - polynomialReDoS(term, pump, prefixMsg, prev) and - result = - "Strings " + prefixMsg + "with many repetitions of '" + pump + - "' can start matching anywhere after the start of the preceeding " + prev -} - -/** - * A term that may cause a regular expression engine to perform a - * polynomial number of match attempts, relative to the input length. - */ -class PolynomialBackTrackingTerm extends InfiniteRepetitionQuantifier { - string reason; - string pump; - string prefixMsg; - RegExpTerm prev; - - PolynomialBackTrackingTerm() { - reason = getReasonString(this, pump, prefixMsg, prev) and - // there might be many reasons for this term to have polynomial backtracking - we pick the shortest one. - reason = min(string msg | msg = getReasonString(this, _, _, _) | msg order by msg.length(), msg) - } - - /** - * Holds if all non-empty successors to the polynomial backtracking term matches the end of the line. - */ - predicate isAtEndLine() { - forall(RegExpTerm succ | this.getSuccessor+() = succ and not matchesEpsilon(succ) | - succ instanceof RegExpDollar - ) - } - - /** - * Gets the string that should be repeated to cause this regular expression to perform polynomially. - */ - string getPumpString() { result = pump } - - /** - * Gets a message for which prefix a matching string must start with for this term to cause polynomial backtracking. - */ - string getPrefixMessage() { result = prefixMsg } - - /** - * Gets a predecessor to `this`, which also loops on the pump string, and thereby causes polynomial backtracking. - */ - RegExpTerm getPreviousLoop() { result = prev } - - /** - * Gets the reason for the number of match attempts. - */ - string getReason() { result = reason } -} +private import semmle.code.java.regex.RegexTreeView::RegexTreeView as TreeView +// SuperlinearBackTracking should be used directly from the shared pack, and not from this file. +deprecated private import codeql.regex.nfa.SuperlinearBackTracking::Make as Dep +import Dep diff --git a/java/ql/lib/upgrades/709f1d1fd04ffd9bbcf242f17b120f8a389949bd/old.dbscheme b/java/ql/lib/upgrades/709f1d1fd04ffd9bbcf242f17b120f8a389949bd/old.dbscheme new file mode 100644 index 00000000000..709f1d1fd04 --- /dev/null +++ b/java/ql/lib/upgrades/709f1d1fd04ffd9bbcf242f17b120f8a389949bd/old.dbscheme @@ -0,0 +1,1240 @@ +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * javac A.java B.java C.java + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * javac A.java B.java C.java + */ + unique int id : @compilation, + int kind: int ref, + string cwd : string ref, + string name : string ref +); + +case @compilation.kind of + 1 = @javacompilation +| 2 = @kotlincompilation +; + +compilation_started( + int id : @compilation ref +) + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--javac-args` + * 2 | A.java + * 3 | B.java + * 4 | C.java + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | A.java + * 1 | B.java + * 2 | C.java + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * For each file recorded in `compilation_compiling_files`, + * there will be a corresponding row in + * `compilation_compiling_files_completed` once extraction + * of that file is complete. The `result` will indicate the + * extraction result: + * + * 0: Successfully extracted + * 1: Errors were encountered, but extraction recovered + * 2: Errors were encountered, and extraction could not recover + */ +#keyset[id, num] +compilation_compiling_files_completed( + int id : @compilation ref, + int num : int ref, + int result : int ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * The `cpu_seconds` and `elapsed_seconds` are the CPU time and elapsed + * time (respectively) that the original compilation (not the extraction) + * took for compiler invocation `id`. + */ +compilation_compiler_times( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + * The `result` will indicate the extraction result: + * + * 0: Successfully extracted + * 1: Errors were encountered, but extraction recovered + * 2: Errors were encountered, and extraction could not recover + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref, + int result : int ref +); + +diagnostics( + unique int id: @diagnostic, + string generated_by: string ref, // TODO: Sync this with the other languages? + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +/* + * External artifacts + */ + +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +snapshotDate( + unique date snapshotDate : date ref +); + +sourceLocationPrefix( + string prefix : string ref +); + +/* + * Duplicate code + */ + +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +@duplication_or_similarity = @duplication | @similarity + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/* + * SMAP + */ + +smap_header( + int outputFileId: @file ref, + string outputFilename: string ref, + string defaultStratum: string ref +); + +smap_files( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + string inputFileName: string ref, + int inputFileId: @file ref +); + +smap_lines( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + int inputStartLine: int ref, + int inputLineCount: int ref, + int outputStartLine: int ref, + int outputLineIncrement: int ref +); + +/* + * Locations and files + */ + +@location = @location_default ; + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +hasLocation( + int locatableid: @locatable ref, + int id: @location ref +); + +@sourceline = @locatable ; + +#keyset[element_id] +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +/* + * Java + */ + +cupackage( + unique int id: @file ref, + int packageid: @package ref +); + +#keyset[fileid,keyName] +jarManifestMain( + int fileid: @file ref, + string keyName: string ref, + string value: string ref +); + +#keyset[fileid,entryName,keyName] +jarManifestEntries( + int fileid: @file ref, + string entryName: string ref, + string keyName: string ref, + string value: string ref +); + +packages( + unique int id: @package, + string nodeName: string ref +); + +primitives( + unique int id: @primitive, + string nodeName: string ref +); + +modifiers( + unique int id: @modifier, + string nodeName: string ref +); + +/** + * An errortype is used when the extractor is unable to extract a type + * correctly for some reason. + */ +error_type( + unique int id: @errortype +); + +classes( + unique int id: @class, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @class ref +); + +file_class( + int id: @class ref +); + +class_object( + unique int id: @class ref, + unique int instance: @field ref +); + +type_companion_object( + unique int id: @classorinterface ref, + unique int instance: @field ref, + unique int companion_object: @class ref +); + +kt_nullable_types( + unique int id: @kt_nullable_type, + int classid: @reftype ref +) + +kt_notnull_types( + unique int id: @kt_notnull_type, + int classid: @reftype ref +) + +kt_type_alias( + unique int id: @kt_type_alias, + string name: string ref, + int kttypeid: @kt_type ref +) + +@kt_type = @kt_nullable_type | @kt_notnull_type + +isRecord( + unique int id: @class ref +); + +interfaces( + unique int id: @interface, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @interface ref +); + +fielddecls( + unique int id: @fielddecl, + int parentid: @reftype ref +); + +#keyset[fieldId] #keyset[fieldDeclId,pos] +fieldDeclaredIn( + int fieldId: @field ref, + int fieldDeclId: @fielddecl ref, + int pos: int ref +); + +fields( + unique int id: @field, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @field ref +); + +fieldsKotlinType( + unique int id: @field ref, + int kttypeid: @kt_type ref +); + +constrs( + unique int id: @constructor, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @constructor ref +); + +constrsKotlinType( + unique int id: @constructor ref, + int kttypeid: @kt_type ref +); + +methods( + unique int id: @method, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @method ref +); + +methodsKotlinType( + unique int id: @method ref, + int kttypeid: @kt_type ref +); + +#keyset[parentid,pos] +params( + unique int id: @param, + int typeid: @type ref, + int pos: int ref, + int parentid: @callable ref, + int sourceid: @param ref +); + +paramsKotlinType( + unique int id: @param ref, + int kttypeid: @kt_type ref +); + +paramName( + unique int id: @param ref, + string nodeName: string ref +); + +isVarargsParam( + int param: @param ref +); + +exceptions( + unique int id: @exception, + int typeid: @type ref, + int parentid: @callable ref +); + +isAnnotType( + int interfaceid: @interface ref +); + +isAnnotElem( + int methodid: @method ref +); + +annotValue( + int parentid: @annotation ref, + int id2: @method ref, + unique int value: @expr ref +); + +isEnumType( + int classid: @class ref +); + +isEnumConst( + int fieldid: @field ref +); + +#keyset[parentid,pos] +typeVars( + unique int id: @typevariable, + string nodeName: string ref, + int pos: int ref, + int kind: int ref, // deprecated + int parentid: @classorinterfaceorcallable ref +); + +wildcards( + unique int id: @wildcard, + string nodeName: string ref, + int kind: int ref +); + +#keyset[parentid,pos] +typeBounds( + unique int id: @typebound, + int typeid: @reftype ref, + int pos: int ref, + int parentid: @boundedtype ref +); + +#keyset[parentid,pos] +typeArgs( + int argumentid: @reftype ref, + int pos: int ref, + int parentid: @classorinterfaceorcallable ref +); + +isParameterized( + int memberid: @member ref +); + +isRaw( + int memberid: @member ref +); + +erasure( + unique int memberid: @member ref, + int erasureid: @member ref +); + +#keyset[classid] #keyset[parent] +isAnonymClass( + int classid: @class ref, + int parent: @classinstancexpr ref +); + +#keyset[typeid] #keyset[parent] +isLocalClassOrInterface( + int typeid: @classorinterface ref, + int parent: @localtypedeclstmt ref +); + +isDefConstr( + int constructorid: @constructor ref +); + +#keyset[exprId] +lambdaKind( + int exprId: @lambdaexpr ref, + int bodyKind: int ref +); + +arrays( + unique int id: @array, + string nodeName: string ref, + int elementtypeid: @type ref, + int dimension: int ref, + int componenttypeid: @type ref +); + +enclInReftype( + unique int child: @reftype ref, + int parent: @reftype ref +); + +extendsReftype( + int id1: @reftype ref, + int id2: @classorinterface ref +); + +implInterface( + int id1: @classorarray ref, + int id2: @interface ref +); + +permits( + int id1: @classorinterface ref, + int id2: @classorinterface ref +); + +hasModifier( + int id1: @modifiable ref, + int id2: @modifier ref +); + +imports( + unique int id: @import, + int holder: @classorinterfaceorpackage ref, + string name: string ref, + int kind: int ref +); + +#keyset[parent,idx] +stmts( + unique int id: @stmt, + int kind: int ref, + int parent: @stmtparent ref, + int idx: int ref, + int bodydecl: @callable ref +); + +@stmtparent = @callable | @stmt | @switchexpr | @whenexpr| @stmtexpr; + +case @stmt.kind of + 0 = @block +| 1 = @ifstmt +| 2 = @forstmt +| 3 = @enhancedforstmt +| 4 = @whilestmt +| 5 = @dostmt +| 6 = @trystmt +| 7 = @switchstmt +| 8 = @synchronizedstmt +| 9 = @returnstmt +| 10 = @throwstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @emptystmt +| 14 = @exprstmt +| 15 = @labeledstmt +| 16 = @assertstmt +| 17 = @localvariabledeclstmt +| 18 = @localtypedeclstmt +| 19 = @constructorinvocationstmt +| 20 = @superconstructorinvocationstmt +| 21 = @case +| 22 = @catchclause +| 23 = @yieldstmt +| 24 = @errorstmt +| 25 = @whenbranch +; + +#keyset[parent,idx] +exprs( + unique int id: @expr, + int kind: int ref, + int typeid: @type ref, + int parent: @exprparent ref, + int idx: int ref +); + +exprsKotlinType( + unique int id: @expr ref, + int kttypeid: @kt_type ref +); + +callableEnclosingExpr( + unique int id: @expr ref, + int callable_id: @callable ref +); + +statementEnclosingExpr( + unique int id: @expr ref, + int statement_id: @stmt ref +); + +isParenthesized( + unique int id: @expr ref, + int parentheses: int ref +); + +case @expr.kind of + 1 = @arrayaccess +| 2 = @arraycreationexpr +| 3 = @arrayinit +| 4 = @assignexpr +| 5 = @assignaddexpr +| 6 = @assignsubexpr +| 7 = @assignmulexpr +| 8 = @assigndivexpr +| 9 = @assignremexpr +| 10 = @assignandexpr +| 11 = @assignorexpr +| 12 = @assignxorexpr +| 13 = @assignlshiftexpr +| 14 = @assignrshiftexpr +| 15 = @assignurshiftexpr +| 16 = @booleanliteral +| 17 = @integerliteral +| 18 = @longliteral +| 19 = @floatingpointliteral +| 20 = @doubleliteral +| 21 = @characterliteral +| 22 = @stringliteral +| 23 = @nullliteral +| 24 = @mulexpr +| 25 = @divexpr +| 26 = @remexpr +| 27 = @addexpr +| 28 = @subexpr +| 29 = @lshiftexpr +| 30 = @rshiftexpr +| 31 = @urshiftexpr +| 32 = @andbitexpr +| 33 = @orbitexpr +| 34 = @xorbitexpr +| 35 = @andlogicalexpr +| 36 = @orlogicalexpr +| 37 = @ltexpr +| 38 = @gtexpr +| 39 = @leexpr +| 40 = @geexpr +| 41 = @eqexpr +| 42 = @neexpr +| 43 = @postincexpr +| 44 = @postdecexpr +| 45 = @preincexpr +| 46 = @predecexpr +| 47 = @minusexpr +| 48 = @plusexpr +| 49 = @bitnotexpr +| 50 = @lognotexpr +| 51 = @castexpr +| 52 = @newexpr +| 53 = @conditionalexpr +| 54 = @parexpr // deprecated +| 55 = @instanceofexpr +| 56 = @localvariabledeclexpr +| 57 = @typeliteral +| 58 = @thisaccess +| 59 = @superaccess +| 60 = @varaccess +| 61 = @methodaccess +| 62 = @unannotatedtypeaccess +| 63 = @arraytypeaccess +| 64 = @packageaccess +| 65 = @wildcardtypeaccess +| 66 = @declannotation +| 67 = @uniontypeaccess +| 68 = @lambdaexpr +| 69 = @memberref +| 70 = @annotatedtypeaccess +| 71 = @typeannotation +| 72 = @intersectiontypeaccess +| 73 = @switchexpr +| 74 = @errorexpr +| 75 = @whenexpr +| 76 = @getclassexpr +| 77 = @safecastexpr +| 78 = @implicitcastexpr +| 79 = @implicitnotnullexpr +| 80 = @implicitcoerciontounitexpr +| 81 = @notinstanceofexpr +| 82 = @stmtexpr +| 83 = @stringtemplateexpr +| 84 = @notnullexpr +| 85 = @unsafecoerceexpr +| 86 = @valueeqexpr +| 87 = @valueneexpr +| 88 = @propertyref +; + +/** Holds if this `when` expression was written as an `if` expression. */ +when_if(unique int id: @whenexpr ref); + +/** Holds if this `when` branch was written as an `else` branch. */ +when_branch_else(unique int id: @whenbranch ref); + +@classinstancexpr = @newexpr | @lambdaexpr | @memberref | @propertyref + +@annotation = @declannotation | @typeannotation +@typeaccess = @unannotatedtypeaccess | @annotatedtypeaccess + +@assignment = @assignexpr + | @assignop; + +@unaryassignment = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr; + +@assignop = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + | @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignurshiftexpr; + +@literal = @booleanliteral + | @integerliteral + | @longliteral + | @floatingpointliteral + | @doubleliteral + | @characterliteral + | @stringliteral + | @nullliteral; + +@binaryexpr = @mulexpr + | @divexpr + | @remexpr + | @addexpr + | @subexpr + | @lshiftexpr + | @rshiftexpr + | @urshiftexpr + | @andbitexpr + | @orbitexpr + | @xorbitexpr + | @andlogicalexpr + | @orlogicalexpr + | @ltexpr + | @gtexpr + | @leexpr + | @geexpr + | @eqexpr + | @neexpr + | @valueeqexpr + | @valueneexpr; + +@unaryexpr = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr + | @minusexpr + | @plusexpr + | @bitnotexpr + | @lognotexpr + | @notnullexpr; + +@caller = @classinstancexpr + | @methodaccess + | @constructorinvocationstmt + | @superconstructorinvocationstmt; + +callableBinding( + unique int callerid: @caller ref, + int callee: @callable ref +); + +memberRefBinding( + unique int id: @expr ref, + int callable: @callable ref +); + +propertyRefGetBinding( + unique int id: @expr ref, + int getter: @callable ref +); + +propertyRefFieldBinding( + unique int id: @expr ref, + int field: @field ref +); + +propertyRefSetBinding( + unique int id: @expr ref, + int setter: @callable ref +); + +@exprparent = @stmt | @expr | @whenbranch | @callable | @field | @fielddecl | @class | @interface | @param | @localvar | @typevariable; + +variableBinding( + unique int expr: @varaccess ref, + int variable: @variable ref +); + +@variable = @localscopevariable | @field; + +@localscopevariable = @localvar | @param; + +localvars( + unique int id: @localvar, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @localvariabledeclexpr ref +); + +localvarsKotlinType( + unique int id: @localvar ref, + int kttypeid: @kt_type ref +); + +@namedexprorstmt = @breakstmt + | @continuestmt + | @labeledstmt + | @literal; + +namestrings( + string name: string ref, + string value: string ref, + unique int parent: @namedexprorstmt ref +); + +/* + * Modules + */ + +#keyset[name] +modules( + unique int id: @module, + string name: string ref +); + +isOpen( + int id: @module ref +); + +#keyset[fileId] +cumodule( + int fileId: @file ref, + int moduleId: @module ref +); + +@directive = @requires + | @exports + | @opens + | @uses + | @provides + +#keyset[directive] +directives( + int id: @module ref, + int directive: @directive ref +); + +requires( + unique int id: @requires, + int target: @module ref +); + +isTransitive( + int id: @requires ref +); + +isStatic( + int id: @requires ref +); + +exports( + unique int id: @exports, + int target: @package ref +); + +exportsTo( + int id: @exports ref, + int target: @module ref +); + +opens( + unique int id: @opens, + int target: @package ref +); + +opensTo( + int id: @opens ref, + int target: @module ref +); + +uses( + unique int id: @uses, + string serviceInterface: string ref +); + +provides( + unique int id: @provides, + string serviceInterface: string ref +); + +providesWith( + int id: @provides ref, + string serviceImpl: string ref +); + +/* + * Javadoc + */ + +javadoc( + unique int id: @javadoc +); + +isNormalComment( + int commentid : @javadoc ref +); + +isEolComment( + int commentid : @javadoc ref +); + +hasJavadoc( + int documentableid: @member ref, + int javadocid: @javadoc ref +); + +#keyset[parentid,idx] +javadocTag( + unique int id: @javadocTag, + string name: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +#keyset[parentid,idx] +javadocText( + unique int id: @javadocText, + string text: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +@javadocParent = @javadoc | @javadocTag; +@javadocElement = @javadocTag | @javadocText; + +@classorinterface = @interface | @class; +@classorinterfaceorpackage = @classorinterface | @package; +@classorinterfaceorcallable = @classorinterface | @callable; +@boundedtype = @typevariable | @wildcard; +@reftype = @classorinterface | @array | @boundedtype | @errortype; +@classorarray = @class | @array; +@type = @primitive | @reftype; +@callable = @method | @constructor; + +/** A program element that has a name. */ +@element = @package | @modifier | @annotation | @errortype | + @locatableElement; + +@locatableElement = @file | @primitive | @class | @interface | @method | @constructor | @param | @exception | @field | + @boundedtype | @array | @localvar | @expr | @stmt | @import | @fielddecl | @kt_type | @kt_type_alias | + @kt_property; + +@modifiable = @member_modifiable| @param | @localvar | @typevariable; + +@member_modifiable = @class | @interface | @method | @constructor | @field | @kt_property; + +@member = @method | @constructor | @field | @reftype ; + +/** A program element that has a location. */ +@locatable = @typebound | @javadoc | @javadocTag | @javadocText | @xmllocatable | @ktcomment | + @locatableElement; + +@top = @element | @locatable | @folder; + +/* + * XML Files + */ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/* + * configuration files with key value pairs + */ + +configs( + unique int id: @config +); + +configNames( + unique int id: @configName, + int config: @config ref, + string name: string ref +); + +configValues( + unique int id: @configValue, + int config: @config ref, + string value: string ref +); + +configLocations( + int locatable: @configLocatable ref, + int location: @location_default ref +); + +@configLocatable = @config | @configName | @configValue; + +ktComments( + unique int id: @ktcomment, + int kind: int ref, + string text : string ref +) + +ktCommentSections( + unique int id: @ktcommentsection, + int comment: @ktcomment ref, + string content : string ref +) + +ktCommentSectionNames( + unique int id: @ktcommentsection ref, + string name : string ref +) + +ktCommentSectionSubjectNames( + unique int id: @ktcommentsection ref, + string subjectname : string ref +) + +#keyset[id, owner] +ktCommentOwners( + int id: @ktcomment ref, + int owner: @top ref +) + +ktExtensionFunctions( + unique int id: @method ref, + int typeid: @type ref, + int kttypeid: @kt_type ref +) + +ktProperties( + unique int id: @kt_property, + string nodeName: string ref +) + +ktPropertyGetters( + unique int id: @kt_property ref, + int getter: @method ref +) + +ktPropertySetters( + unique int id: @kt_property ref, + int setter: @method ref +) + +ktPropertyBackingFields( + unique int id: @kt_property ref, + int backingField: @field ref +) + +ktSyntheticBody( + unique int id: @callable ref, + int kind: int ref + // 1: ENUM_VALUES + // 2: ENUM_VALUEOF +) + +ktLocalFunction( + unique int id: @method ref +) + +ktInitializerAssignment( + unique int id: @assignexpr ref +) + +ktPropertyDelegates( + unique int id: @kt_property ref, + unique int variableId: @variable ref +) + +/** + * If `id` is a compiler generated element, then the kind indicates the + * reason that the compiler generated it. + * See `Element.compilerGeneratedReason()` for an explanation of what + * each `kind` means. + */ +compiler_generated( + unique int id: @element ref, + int kind: int ref +) + +ktFunctionOriginalNames( + unique int id: @method ref, + string name: string ref +) + +ktDataClasses( + unique int id: @class ref +) diff --git a/java/ql/lib/upgrades/709f1d1fd04ffd9bbcf242f17b120f8a389949bd/semmlecode.dbscheme b/java/ql/lib/upgrades/709f1d1fd04ffd9bbcf242f17b120f8a389949bd/semmlecode.dbscheme new file mode 100644 index 00000000000..44d61b266be --- /dev/null +++ b/java/ql/lib/upgrades/709f1d1fd04ffd9bbcf242f17b120f8a389949bd/semmlecode.dbscheme @@ -0,0 +1,1246 @@ +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * javac A.java B.java C.java + * + * The `id` simply identifies the invocation, while `cwd` is the working + * directory from which the compiler was invoked. + */ +compilations( + /** + * An invocation of the compiler. Note that more than one file may + * be compiled per invocation. For example, this command compiles + * three source files: + * + * javac A.java B.java C.java + */ + unique int id : @compilation, + int kind: int ref, + string cwd : string ref, + string name : string ref +); + +case @compilation.kind of + 1 = @javacompilation +| 2 = @kotlincompilation +; + +compilation_started( + int id : @compilation ref +) + +compilation_info( + int id : @compilation ref, + string info_key: string ref, + string info_value: string ref +) + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--javac-args` + * 2 | A.java + * 3 | B.java + * 4 | C.java + */ +#keyset[id, num] +compilation_args( + int id : @compilation ref, + int num : int ref, + string arg : string ref +); + +/** + * The source files that are compiled by a compiler invocation. + * If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | A.java + * 1 | B.java + * 2 | C.java + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * For each file recorded in `compilation_compiling_files`, + * there will be a corresponding row in + * `compilation_compiling_files_completed` once extraction + * of that file is complete. The `result` will indicate the + * extraction result: + * + * 0: Successfully extracted + * 1: Errors were encountered, but extraction recovered + * 2: Errors were encountered, and extraction could not recover + */ +#keyset[id, num] +compilation_compiling_files_completed( + int id : @compilation ref, + int num : int ref, + int result : int ref +); + +/** + * The time taken by the extractor for a compiler invocation. + * + * For each file `num`, there will be rows for + * + * kind | seconds + * ---- | --- + * 1 | CPU seconds used by the extractor frontend + * 2 | Elapsed seconds during the extractor frontend + * 3 | CPU seconds used by the extractor backend + * 4 | Elapsed seconds during the extractor backend + */ +#keyset[id, num, kind] +compilation_time( + int id : @compilation ref, + int num : int ref, + /* kind: + 1 = frontend_cpu_seconds + 2 = frontend_elapsed_seconds + 3 = extractor_cpu_seconds + 4 = extractor_elapsed_seconds + */ + int kind : int ref, + float seconds : float ref +); + +/** + * An error or warning generated by the extractor. + * The diagnostic message `diagnostic` was generated during compiler + * invocation `compilation`, and is the `file_number_diagnostic_number`th + * message generated while extracting the `file_number`th file of that + * invocation. + */ +#keyset[compilation, file_number, file_number_diagnostic_number] +diagnostic_for( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * The `cpu_seconds` and `elapsed_seconds` are the CPU time and elapsed + * time (respectively) that the original compilation (not the extraction) + * took for compiler invocation `id`. + */ +compilation_compiler_times( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref +); + +/** + * If extraction was successful, then `cpu_seconds` and + * `elapsed_seconds` are the CPU time and elapsed time (respectively) + * that extraction took for compiler invocation `id`. + * The `result` will indicate the extraction result: + * + * 0: Successfully extracted + * 1: Errors were encountered, but extraction recovered + * 2: Errors were encountered, and extraction could not recover + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref, + int result : int ref +); + +diagnostics( + unique int id: @diagnostic, + string generated_by: string ref, // TODO: Sync this with the other languages? + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +/* + * External artifacts + */ + +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +snapshotDate( + unique date snapshotDate : date ref +); + +sourceLocationPrefix( + string prefix : string ref +); + +/* + * Duplicate code + */ + +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +@duplication_or_similarity = @duplication | @similarity + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/* + * SMAP + */ + +smap_header( + int outputFileId: @file ref, + string outputFilename: string ref, + string defaultStratum: string ref +); + +smap_files( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + string inputFileName: string ref, + int inputFileId: @file ref +); + +smap_lines( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + int inputStartLine: int ref, + int inputLineCount: int ref, + int outputStartLine: int ref, + int outputLineIncrement: int ref +); + +/* + * Locations and files + */ + +@location = @location_default ; + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +hasLocation( + int locatableid: @locatable ref, + int id: @location ref +); + +@sourceline = @locatable ; + +#keyset[element_id] +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @folder | @file + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +/* + * Java + */ + +cupackage( + unique int id: @file ref, + int packageid: @package ref +); + +#keyset[fileid,keyName] +jarManifestMain( + int fileid: @file ref, + string keyName: string ref, + string value: string ref +); + +#keyset[fileid,entryName,keyName] +jarManifestEntries( + int fileid: @file ref, + string entryName: string ref, + string keyName: string ref, + string value: string ref +); + +packages( + unique int id: @package, + string nodeName: string ref +); + +primitives( + unique int id: @primitive, + string nodeName: string ref +); + +modifiers( + unique int id: @modifier, + string nodeName: string ref +); + +/** + * An errortype is used when the extractor is unable to extract a type + * correctly for some reason. + */ +error_type( + unique int id: @errortype +); + +classes( + unique int id: @class, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @class ref +); + +file_class( + int id: @class ref +); + +class_object( + unique int id: @class ref, + unique int instance: @field ref +); + +type_companion_object( + unique int id: @classorinterface ref, + unique int instance: @field ref, + unique int companion_object: @class ref +); + +kt_nullable_types( + unique int id: @kt_nullable_type, + int classid: @reftype ref +) + +kt_notnull_types( + unique int id: @kt_notnull_type, + int classid: @reftype ref +) + +kt_type_alias( + unique int id: @kt_type_alias, + string name: string ref, + int kttypeid: @kt_type ref +) + +@kt_type = @kt_nullable_type | @kt_notnull_type + +isRecord( + unique int id: @class ref +); + +interfaces( + unique int id: @interface, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @interface ref +); + +fielddecls( + unique int id: @fielddecl, + int parentid: @reftype ref +); + +#keyset[fieldId] #keyset[fieldDeclId,pos] +fieldDeclaredIn( + int fieldId: @field ref, + int fieldDeclId: @fielddecl ref, + int pos: int ref +); + +fields( + unique int id: @field, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @field ref +); + +fieldsKotlinType( + unique int id: @field ref, + int kttypeid: @kt_type ref +); + +constrs( + unique int id: @constructor, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @constructor ref +); + +constrsKotlinType( + unique int id: @constructor ref, + int kttypeid: @kt_type ref +); + +methods( + unique int id: @method, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @method ref +); + +methodsKotlinType( + unique int id: @method ref, + int kttypeid: @kt_type ref +); + +#keyset[parentid,pos] +params( + unique int id: @param, + int typeid: @type ref, + int pos: int ref, + int parentid: @callable ref, + int sourceid: @param ref +); + +paramsKotlinType( + unique int id: @param ref, + int kttypeid: @kt_type ref +); + +paramName( + unique int id: @param ref, + string nodeName: string ref +); + +isVarargsParam( + int param: @param ref +); + +exceptions( + unique int id: @exception, + int typeid: @type ref, + int parentid: @callable ref +); + +isAnnotType( + int interfaceid: @interface ref +); + +isAnnotElem( + int methodid: @method ref +); + +annotValue( + int parentid: @annotation ref, + int id2: @method ref, + unique int value: @expr ref +); + +isEnumType( + int classid: @class ref +); + +isEnumConst( + int fieldid: @field ref +); + +#keyset[parentid,pos] +typeVars( + unique int id: @typevariable, + string nodeName: string ref, + int pos: int ref, + int kind: int ref, // deprecated + int parentid: @classorinterfaceorcallable ref +); + +wildcards( + unique int id: @wildcard, + string nodeName: string ref, + int kind: int ref +); + +#keyset[parentid,pos] +typeBounds( + unique int id: @typebound, + int typeid: @reftype ref, + int pos: int ref, + int parentid: @boundedtype ref +); + +#keyset[parentid,pos] +typeArgs( + int argumentid: @reftype ref, + int pos: int ref, + int parentid: @classorinterfaceorcallable ref +); + +isParameterized( + int memberid: @member ref +); + +isRaw( + int memberid: @member ref +); + +erasure( + unique int memberid: @member ref, + int erasureid: @member ref +); + +#keyset[classid] #keyset[parent] +isAnonymClass( + int classid: @class ref, + int parent: @classinstancexpr ref +); + +#keyset[typeid] #keyset[parent] +isLocalClassOrInterface( + int typeid: @classorinterface ref, + int parent: @localtypedeclstmt ref +); + +isDefConstr( + int constructorid: @constructor ref +); + +#keyset[exprId] +lambdaKind( + int exprId: @lambdaexpr ref, + int bodyKind: int ref +); + +arrays( + unique int id: @array, + string nodeName: string ref, + int elementtypeid: @type ref, + int dimension: int ref, + int componenttypeid: @type ref +); + +enclInReftype( + unique int child: @reftype ref, + int parent: @reftype ref +); + +extendsReftype( + int id1: @reftype ref, + int id2: @classorinterface ref +); + +implInterface( + int id1: @classorarray ref, + int id2: @interface ref +); + +permits( + int id1: @classorinterface ref, + int id2: @classorinterface ref +); + +hasModifier( + int id1: @modifiable ref, + int id2: @modifier ref +); + +imports( + unique int id: @import, + int holder: @classorinterfaceorpackage ref, + string name: string ref, + int kind: int ref +); + +#keyset[parent,idx] +stmts( + unique int id: @stmt, + int kind: int ref, + int parent: @stmtparent ref, + int idx: int ref, + int bodydecl: @callable ref +); + +@stmtparent = @callable | @stmt | @switchexpr | @whenexpr| @stmtexpr; + +case @stmt.kind of + 0 = @block +| 1 = @ifstmt +| 2 = @forstmt +| 3 = @enhancedforstmt +| 4 = @whilestmt +| 5 = @dostmt +| 6 = @trystmt +| 7 = @switchstmt +| 8 = @synchronizedstmt +| 9 = @returnstmt +| 10 = @throwstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @emptystmt +| 14 = @exprstmt +| 15 = @labeledstmt +| 16 = @assertstmt +| 17 = @localvariabledeclstmt +| 18 = @localtypedeclstmt +| 19 = @constructorinvocationstmt +| 20 = @superconstructorinvocationstmt +| 21 = @case +| 22 = @catchclause +| 23 = @yieldstmt +| 24 = @errorstmt +| 25 = @whenbranch +; + +#keyset[parent,idx] +exprs( + unique int id: @expr, + int kind: int ref, + int typeid: @type ref, + int parent: @exprparent ref, + int idx: int ref +); + +exprsKotlinType( + unique int id: @expr ref, + int kttypeid: @kt_type ref +); + +callableEnclosingExpr( + unique int id: @expr ref, + int callable_id: @callable ref +); + +statementEnclosingExpr( + unique int id: @expr ref, + int statement_id: @stmt ref +); + +isParenthesized( + unique int id: @expr ref, + int parentheses: int ref +); + +case @expr.kind of + 1 = @arrayaccess +| 2 = @arraycreationexpr +| 3 = @arrayinit +| 4 = @assignexpr +| 5 = @assignaddexpr +| 6 = @assignsubexpr +| 7 = @assignmulexpr +| 8 = @assigndivexpr +| 9 = @assignremexpr +| 10 = @assignandexpr +| 11 = @assignorexpr +| 12 = @assignxorexpr +| 13 = @assignlshiftexpr +| 14 = @assignrshiftexpr +| 15 = @assignurshiftexpr +| 16 = @booleanliteral +| 17 = @integerliteral +| 18 = @longliteral +| 19 = @floatingpointliteral +| 20 = @doubleliteral +| 21 = @characterliteral +| 22 = @stringliteral +| 23 = @nullliteral +| 24 = @mulexpr +| 25 = @divexpr +| 26 = @remexpr +| 27 = @addexpr +| 28 = @subexpr +| 29 = @lshiftexpr +| 30 = @rshiftexpr +| 31 = @urshiftexpr +| 32 = @andbitexpr +| 33 = @orbitexpr +| 34 = @xorbitexpr +| 35 = @andlogicalexpr +| 36 = @orlogicalexpr +| 37 = @ltexpr +| 38 = @gtexpr +| 39 = @leexpr +| 40 = @geexpr +| 41 = @eqexpr +| 42 = @neexpr +| 43 = @postincexpr +| 44 = @postdecexpr +| 45 = @preincexpr +| 46 = @predecexpr +| 47 = @minusexpr +| 48 = @plusexpr +| 49 = @bitnotexpr +| 50 = @lognotexpr +| 51 = @castexpr +| 52 = @newexpr +| 53 = @conditionalexpr +| 54 = @parexpr // deprecated +| 55 = @instanceofexpr +| 56 = @localvariabledeclexpr +| 57 = @typeliteral +| 58 = @thisaccess +| 59 = @superaccess +| 60 = @varaccess +| 61 = @methodaccess +| 62 = @unannotatedtypeaccess +| 63 = @arraytypeaccess +| 64 = @packageaccess +| 65 = @wildcardtypeaccess +| 66 = @declannotation +| 67 = @uniontypeaccess +| 68 = @lambdaexpr +| 69 = @memberref +| 70 = @annotatedtypeaccess +| 71 = @typeannotation +| 72 = @intersectiontypeaccess +| 73 = @switchexpr +| 74 = @errorexpr +| 75 = @whenexpr +| 76 = @getclassexpr +| 77 = @safecastexpr +| 78 = @implicitcastexpr +| 79 = @implicitnotnullexpr +| 80 = @implicitcoerciontounitexpr +| 81 = @notinstanceofexpr +| 82 = @stmtexpr +| 83 = @stringtemplateexpr +| 84 = @notnullexpr +| 85 = @unsafecoerceexpr +| 86 = @valueeqexpr +| 87 = @valueneexpr +| 88 = @propertyref +; + +/** Holds if this `when` expression was written as an `if` expression. */ +when_if(unique int id: @whenexpr ref); + +/** Holds if this `when` branch was written as an `else` branch. */ +when_branch_else(unique int id: @whenbranch ref); + +@classinstancexpr = @newexpr | @lambdaexpr | @memberref | @propertyref + +@annotation = @declannotation | @typeannotation +@typeaccess = @unannotatedtypeaccess | @annotatedtypeaccess + +@assignment = @assignexpr + | @assignop; + +@unaryassignment = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr; + +@assignop = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + | @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignurshiftexpr; + +@literal = @booleanliteral + | @integerliteral + | @longliteral + | @floatingpointliteral + | @doubleliteral + | @characterliteral + | @stringliteral + | @nullliteral; + +@binaryexpr = @mulexpr + | @divexpr + | @remexpr + | @addexpr + | @subexpr + | @lshiftexpr + | @rshiftexpr + | @urshiftexpr + | @andbitexpr + | @orbitexpr + | @xorbitexpr + | @andlogicalexpr + | @orlogicalexpr + | @ltexpr + | @gtexpr + | @leexpr + | @geexpr + | @eqexpr + | @neexpr + | @valueeqexpr + | @valueneexpr; + +@unaryexpr = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr + | @minusexpr + | @plusexpr + | @bitnotexpr + | @lognotexpr + | @notnullexpr; + +@caller = @classinstancexpr + | @methodaccess + | @constructorinvocationstmt + | @superconstructorinvocationstmt; + +callableBinding( + unique int callerid: @caller ref, + int callee: @callable ref +); + +memberRefBinding( + unique int id: @expr ref, + int callable: @callable ref +); + +propertyRefGetBinding( + unique int id: @expr ref, + int getter: @callable ref +); + +propertyRefFieldBinding( + unique int id: @expr ref, + int field: @field ref +); + +propertyRefSetBinding( + unique int id: @expr ref, + int setter: @callable ref +); + +@exprparent = @stmt | @expr | @whenbranch | @callable | @field | @fielddecl | @class | @interface | @param | @localvar | @typevariable; + +variableBinding( + unique int expr: @varaccess ref, + int variable: @variable ref +); + +@variable = @localscopevariable | @field; + +@localscopevariable = @localvar | @param; + +localvars( + unique int id: @localvar, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @localvariabledeclexpr ref +); + +localvarsKotlinType( + unique int id: @localvar ref, + int kttypeid: @kt_type ref +); + +@namedexprorstmt = @breakstmt + | @continuestmt + | @labeledstmt + | @literal; + +namestrings( + string name: string ref, + string value: string ref, + unique int parent: @namedexprorstmt ref +); + +/* + * Modules + */ + +#keyset[name] +modules( + unique int id: @module, + string name: string ref +); + +isOpen( + int id: @module ref +); + +#keyset[fileId] +cumodule( + int fileId: @file ref, + int moduleId: @module ref +); + +@directive = @requires + | @exports + | @opens + | @uses + | @provides + +#keyset[directive] +directives( + int id: @module ref, + int directive: @directive ref +); + +requires( + unique int id: @requires, + int target: @module ref +); + +isTransitive( + int id: @requires ref +); + +isStatic( + int id: @requires ref +); + +exports( + unique int id: @exports, + int target: @package ref +); + +exportsTo( + int id: @exports ref, + int target: @module ref +); + +opens( + unique int id: @opens, + int target: @package ref +); + +opensTo( + int id: @opens ref, + int target: @module ref +); + +uses( + unique int id: @uses, + string serviceInterface: string ref +); + +provides( + unique int id: @provides, + string serviceInterface: string ref +); + +providesWith( + int id: @provides ref, + string serviceImpl: string ref +); + +/* + * Javadoc + */ + +javadoc( + unique int id: @javadoc +); + +isNormalComment( + int commentid : @javadoc ref +); + +isEolComment( + int commentid : @javadoc ref +); + +hasJavadoc( + int documentableid: @member ref, + int javadocid: @javadoc ref +); + +#keyset[parentid,idx] +javadocTag( + unique int id: @javadocTag, + string name: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +#keyset[parentid,idx] +javadocText( + unique int id: @javadocText, + string text: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +@javadocParent = @javadoc | @javadocTag; +@javadocElement = @javadocTag | @javadocText; + +@classorinterface = @interface | @class; +@classorinterfaceorpackage = @classorinterface | @package; +@classorinterfaceorcallable = @classorinterface | @callable; +@boundedtype = @typevariable | @wildcard; +@reftype = @classorinterface | @array | @boundedtype | @errortype; +@classorarray = @class | @array; +@type = @primitive | @reftype; +@callable = @method | @constructor; + +/** A program element that has a name. */ +@element = @package | @modifier | @annotation | @errortype | + @locatableElement; + +@locatableElement = @file | @primitive | @class | @interface | @method | @constructor | @param | @exception | @field | + @boundedtype | @array | @localvar | @expr | @stmt | @import | @fielddecl | @kt_type | @kt_type_alias | + @kt_property; + +@modifiable = @member_modifiable| @param | @localvar | @typevariable; + +@member_modifiable = @class | @interface | @method | @constructor | @field | @kt_property; + +@member = @method | @constructor | @field | @reftype ; + +/** A program element that has a location. */ +@locatable = @typebound | @javadoc | @javadocTag | @javadocText | @xmllocatable | @ktcomment | + @locatableElement; + +@top = @element | @locatable | @folder; + +/* + * XML Files + */ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +/* + * configuration files with key value pairs + */ + +configs( + unique int id: @config +); + +configNames( + unique int id: @configName, + int config: @config ref, + string name: string ref +); + +configValues( + unique int id: @configValue, + int config: @config ref, + string value: string ref +); + +configLocations( + int locatable: @configLocatable ref, + int location: @location_default ref +); + +@configLocatable = @config | @configName | @configValue; + +ktComments( + unique int id: @ktcomment, + int kind: int ref, + string text : string ref +) + +ktCommentSections( + unique int id: @ktcommentsection, + int comment: @ktcomment ref, + string content : string ref +) + +ktCommentSectionNames( + unique int id: @ktcommentsection ref, + string name : string ref +) + +ktCommentSectionSubjectNames( + unique int id: @ktcommentsection ref, + string subjectname : string ref +) + +#keyset[id, owner] +ktCommentOwners( + int id: @ktcomment ref, + int owner: @top ref +) + +ktExtensionFunctions( + unique int id: @method ref, + int typeid: @type ref, + int kttypeid: @kt_type ref +) + +ktProperties( + unique int id: @kt_property, + string nodeName: string ref +) + +ktPropertyGetters( + unique int id: @kt_property ref, + int getter: @method ref +) + +ktPropertySetters( + unique int id: @kt_property ref, + int setter: @method ref +) + +ktPropertyBackingFields( + unique int id: @kt_property ref, + int backingField: @field ref +) + +ktSyntheticBody( + unique int id: @callable ref, + int kind: int ref + // 1: ENUM_VALUES + // 2: ENUM_VALUEOF +) + +ktLocalFunction( + unique int id: @method ref +) + +ktInitializerAssignment( + unique int id: @assignexpr ref +) + +ktPropertyDelegates( + unique int id: @kt_property ref, + unique int variableId: @variable ref +) + +/** + * If `id` is a compiler generated element, then the kind indicates the + * reason that the compiler generated it. + * See `Element.compilerGeneratedReason()` for an explanation of what + * each `kind` means. + */ +compiler_generated( + unique int id: @element ref, + int kind: int ref +) + +ktFunctionOriginalNames( + unique int id: @method ref, + string name: string ref +) + +ktDataClasses( + unique int id: @class ref +) diff --git a/java/ql/lib/upgrades/709f1d1fd04ffd9bbcf242f17b120f8a389949bd/upgrade.properties b/java/ql/lib/upgrades/709f1d1fd04ffd9bbcf242f17b120f8a389949bd/upgrade.properties new file mode 100644 index 00000000000..1c05ac39dbe --- /dev/null +++ b/java/ql/lib/upgrades/709f1d1fd04ffd9bbcf242f17b120f8a389949bd/upgrade.properties @@ -0,0 +1,2 @@ +description: Add compilation_info +compatibility: backwards diff --git a/java/ql/src/Security/CWE/CWE-020/OverlyLargeRange.ql b/java/ql/src/Security/CWE/CWE-020/OverlyLargeRange.ql index d054659892c..b8ea3e52dbd 100644 --- a/java/ql/src/Security/CWE/CWE-020/OverlyLargeRange.ql +++ b/java/ql/src/Security/CWE/CWE-020/OverlyLargeRange.ql @@ -12,14 +12,15 @@ * external/cwe/cwe-020 */ -import semmle.code.java.security.OverlyLargeRangeQuery +private import semmle.code.java.regex.RegexTreeView::RegexTreeView as TreeView +import codeql.regex.OverlyLargeRangeQuery::Make -RegExpCharacterClass potentialMisparsedCharClass() { +TreeView::RegExpCharacterClass potentialMisparsedCharClass() { // nested char classes are currently misparsed - result.getAChild().(RegExpNormalChar).getValue() = "[" + result.getAChild().(TreeView::RegExpNormalChar).getValue() = "[" } -from RegExpCharacterRange range, string reason +from TreeView::RegExpCharacterRange range, string reason where problem(range, reason) and not range.getParent() = potentialMisparsedCharClass() diff --git a/java/ql/src/Security/CWE/CWE-730/PolynomialReDoS.ql b/java/ql/src/Security/CWE/CWE-730/PolynomialReDoS.ql index 75cd8335fac..a84f1c5213e 100644 --- a/java/ql/src/Security/CWE/CWE-730/PolynomialReDoS.ql +++ b/java/ql/src/Security/CWE/CWE-730/PolynomialReDoS.ql @@ -17,7 +17,9 @@ import java import semmle.code.java.security.regexp.PolynomialReDoSQuery import DataFlow::PathGraph -from DataFlow::PathNode source, DataFlow::PathNode sink, PolynomialBackTrackingTerm regexp +from + DataFlow::PathNode source, DataFlow::PathNode sink, + SuperlinearBackTracking::PolynomialBackTrackingTerm regexp where hasPolynomialReDoSResult(source, sink, regexp) select sink, source, sink, "This $@ that depends on a $@ may run slow on strings " + regexp.getPrefixMessage() + diff --git a/java/ql/src/Security/CWE/CWE-730/ReDoS.ql b/java/ql/src/Security/CWE/CWE-730/ReDoS.ql index 23e258e8915..ca4750fc858 100644 --- a/java/ql/src/Security/CWE/CWE-730/ReDoS.ql +++ b/java/ql/src/Security/CWE/CWE-730/ReDoS.ql @@ -14,12 +14,12 @@ * external/cwe/cwe-400 */ -import java -import semmle.code.java.security.regexp.ExponentialBackTracking +private import semmle.code.java.regex.RegexTreeView::RegexTreeView as TreeView +import codeql.regex.nfa.ExponentialBackTracking::Make as ExponentialBackTracking -from RegExpTerm t, string pump, State s, string prefixMsg +from TreeView::RegExpTerm t, string pump, ExponentialBackTracking::State s, string prefixMsg where - hasReDoSResult(t, pump, s, prefixMsg) and + ExponentialBackTracking::hasReDoSResult(t, pump, s, prefixMsg) and // exclude verbose mode regexes for now not t.getRegex().getAMode() = "VERBOSE" select t, diff --git a/java/ql/src/Telemetry/ExtractorInformation.ql b/java/ql/src/Telemetry/ExtractorInformation.ql index 0eb420ba651..48eb49a1b07 100644 --- a/java/ql/src/Telemetry/ExtractorInformation.ql +++ b/java/ql/src/Telemetry/ExtractorInformation.ql @@ -9,6 +9,13 @@ import java import semmle.code.java.Diagnostics +predicate compilationInfo(string key, int value) { + exists(Compilation c, string infoKey | + key = infoKey + ": " + c.getInfo(infoKey) and + value = 1 + ) +} + predicate fileCount(string key, int value) { key = "Number of files" and value = strictcount(File f) @@ -53,13 +60,38 @@ predicate extractorDiagnostics(string key, int value) { ) } +/* + * Just counting the diagnostics doesn't give the full picture, as + * CODEQL_EXTRACTOR_KOTLIN_DIAGNOSTIC_LIMIT means that some diagnostics + * will be suppressed. In that case, we need to look for the + * suppression message, uncount those that did get emitted, uncount the + * suppression message itself, and then add on the full count. + */ + +predicate extractorTotalDiagnostics(string key, int value) { + exists(string extractor, string limitRegex | + limitRegex = "Total of ([0-9]+) diagnostics \\(reached limit of ([0-9]+)\\).*" and + key = "Total number of diagnostics from " + extractor and + value = + strictcount(Diagnostic d | d.getGeneratedBy() = extractor) + + sum(Diagnostic d | + d.getGeneratedBy() = extractor + | + d.getMessage().regexpCapture(limitRegex, 1).toInt() - + d.getMessage().regexpCapture(limitRegex, 2).toInt() - 1 + ) + ) +} + from string key, int value where + compilationInfo(key, value) or fileCount(key, value) or fileCountByExtension(key, value) or totalNumberOfLines(key, value) or numberOfLinesOfCode(key, value) or totalNumberOfLinesByExtension(key, value) or numberOfLinesOfCodeByExtension(key, value) or - extractorDiagnostics(key, value) + extractorDiagnostics(key, value) or + extractorTotalDiagnostics(key, value) select key, value diff --git a/java/ql/src/utils/model-generator/CaptureNegativeSummaryModels.ql b/java/ql/src/utils/model-generator/CaptureNegativeSummaryModels.ql index 49ed16aa1dc..a6f6e5f26e5 100644 --- a/java/ql/src/utils/model-generator/CaptureNegativeSummaryModels.ql +++ b/java/ql/src/utils/model-generator/CaptureNegativeSummaryModels.ql @@ -6,8 +6,8 @@ * @tags model-generator */ -import internal.CaptureModels -import internal.CaptureSummaryFlow +import utils.modelgenerator.internal.CaptureModels +import utils.modelgenerator.internal.CaptureSummaryFlow from DataFlowTargetApi api, string noflow where noflow = captureNoFlow(api) diff --git a/java/ql/src/utils/model-generator/CaptureSinkModels.ql b/java/ql/src/utils/model-generator/CaptureSinkModels.ql index 740e5aa119a..f047a8a13af 100644 --- a/java/ql/src/utils/model-generator/CaptureSinkModels.ql +++ b/java/ql/src/utils/model-generator/CaptureSinkModels.ql @@ -6,7 +6,7 @@ * @tags model-generator */ -import internal.CaptureModels +import utils.modelgenerator.internal.CaptureModels class Activate extends ActiveConfiguration { override predicate activateToSinkConfig() { any() } diff --git a/java/ql/src/utils/model-generator/CaptureSourceModels.ql b/java/ql/src/utils/model-generator/CaptureSourceModels.ql index 77a00602f3f..7dfc8e0ad34 100644 --- a/java/ql/src/utils/model-generator/CaptureSourceModels.ql +++ b/java/ql/src/utils/model-generator/CaptureSourceModels.ql @@ -6,7 +6,7 @@ * @tags model-generator */ -import internal.CaptureModels +import utils.modelgenerator.internal.CaptureModels class Activate extends ActiveConfiguration { override predicate activateFromSourceConfig() { any() } diff --git a/java/ql/src/utils/model-generator/CaptureSummaryModels.ql b/java/ql/src/utils/model-generator/CaptureSummaryModels.ql index 981c6fe73fc..a8d23ca5b34 100644 --- a/java/ql/src/utils/model-generator/CaptureSummaryModels.ql +++ b/java/ql/src/utils/model-generator/CaptureSummaryModels.ql @@ -6,8 +6,8 @@ * @tags model-generator */ -import internal.CaptureModels -import internal.CaptureSummaryFlow +import utils.modelgenerator.internal.CaptureModels +import utils.modelgenerator.internal.CaptureSummaryFlow from DataFlowTargetApi api, string flow where flow = captureFlow(api) diff --git a/java/ql/src/utils/model-generator/CaptureTypeBasedSummaryModels.ql b/java/ql/src/utils/model-generator/CaptureTypeBasedSummaryModels.ql new file mode 100644 index 00000000000..1cdb5fe959f --- /dev/null +++ b/java/ql/src/utils/model-generator/CaptureTypeBasedSummaryModels.ql @@ -0,0 +1,13 @@ +/** + * @name Capture typed based summary models. + * @description Finds applicable summary models to be used by other queries. + * @kind diagnostic + * @id java/utils/model-generator/summary-models-typed-based + * @tags model-generator + */ + +import utils.modelgenerator.internal.CaptureTypeBasedSummaryModels + +from TypeBasedFlowTargetApi api, string flow +where flow = captureFlow(api) +select flow order by flow diff --git a/java/ql/src/utils/model-generator/internal/CaptureModels.qll b/java/ql/src/utils/modelgenerator/internal/CaptureModels.qll similarity index 100% rename from java/ql/src/utils/model-generator/internal/CaptureModels.qll rename to java/ql/src/utils/modelgenerator/internal/CaptureModels.qll diff --git a/java/ql/src/utils/model-generator/internal/CaptureModelsSpecific.qll b/java/ql/src/utils/modelgenerator/internal/CaptureModelsSpecific.qll similarity index 98% rename from java/ql/src/utils/model-generator/internal/CaptureModelsSpecific.qll rename to java/ql/src/utils/modelgenerator/internal/CaptureModelsSpecific.qll index 64516fbaded..6e9fe7c29b2 100644 --- a/java/ql/src/utils/model-generator/internal/CaptureModelsSpecific.qll +++ b/java/ql/src/utils/modelgenerator/internal/CaptureModelsSpecific.qll @@ -67,6 +67,8 @@ private predicate isRelevantForModels(J::Callable api) { */ predicate isRelevantForDataFlowModels = isRelevantForModels/1; +predicate isRelevantForTypeBasedFlowModels = isRelevantForModels/1; + /** * A class of Callables that are relevant for generating summary, source and sinks models for. * @@ -141,7 +143,7 @@ string asPartialNegativeModel(TargetApiSpecific api) { ) } -private predicate isPrimitiveTypeUsedForBulkData(J::Type t) { +predicate isPrimitiveTypeUsedForBulkData(J::Type t) { t.hasName(["byte", "char", "Byte", "Character"]) } diff --git a/java/ql/src/utils/model-generator/internal/CaptureSummaryFlow.qll b/java/ql/src/utils/modelgenerator/internal/CaptureSummaryFlow.qll similarity index 100% rename from java/ql/src/utils/model-generator/internal/CaptureSummaryFlow.qll rename to java/ql/src/utils/modelgenerator/internal/CaptureSummaryFlow.qll diff --git a/java/ql/src/utils/modelgenerator/internal/CaptureTypeBasedSummaryModels.qll b/java/ql/src/utils/modelgenerator/internal/CaptureTypeBasedSummaryModels.qll new file mode 100644 index 00000000000..3f7f4dd97e6 --- /dev/null +++ b/java/ql/src/utils/modelgenerator/internal/CaptureTypeBasedSummaryModels.qll @@ -0,0 +1,330 @@ +private import java +private import semmle.code.java.Collections +private import semmle.code.java.dataflow.internal.ContainerFlow +private import CaptureModelsSpecific as Specific +private import CaptureModels + +/** + * A type representing instantiations of class types + * that has a method which returns an iterator. + */ +private class IterableClass extends Class { + private Type elementType; + + IterableClass() { + elementType = + unique(Type et | + exists(Method m, RefType return, GenericType t, int position | m.getDeclaringType() = t | + return = m.getReturnType() and + return.getSourceDeclaration().hasQualifiedName("java.util", "Iterator") and + t.getTypeParameter(position) = return.(ParameterizedType).getTypeArgument(0) and + instantiates(this, t, position, et) + ) + ) + } + + /** + * Returns the iterator element type of `this`. + */ + Type getElementType() { result = elementType } +} + +/** + * Holds if type `bound` is an upper bound for type `t` or equal to `t`. + */ +private predicate isEffectivelyUpperBound(Type t, Type bound) { + t = bound or + t.(Wildcard).getUpperBound().getType() = bound +} + +/** + * Holds if type `bound` is a lower bound for type `t` or equal to `t`. + */ +private predicate isEffectivelyLowerBound(Type t, Type bound) { + t = bound or + t.(Wildcard).getLowerBound().getType() = bound +} + +/** + * Holds if `t` is a container like type of `tv` (eg. `List`). + */ +private predicate genericContainerType(RefType t, TypeVariable tv) { + exists(Type et | + et = + [ + t.(ContainerType).getElementType(), t.(IterableClass).getElementType(), + t.(Array).getElementType() + ] + | + isEffectivelyUpperBound(et, tv) + ) +} + +/** + * Holds if `tv` is a type variable of the immediate type declaring `callable`. + */ +private predicate classTypeParameter(Callable callable, TypeVariable tv) { + callable.getDeclaringType().(GenericType).getATypeParameter() = tv +} + +/** + * Holds if `tv` is type variable of `callable` or the type declaring `callable`. + */ +private predicate localTypeParameter(Callable callable, TypeVariable tv) { + classTypeParameter(callable, tv) or + callable.(GenericCallable).getATypeParameter() = tv +} + +/** + * Gets the access path postfix for `t`. + */ +private string getAccessPath(Type t) { + if + t instanceof Array and + not Specific::isPrimitiveTypeUsedForBulkData(t.(Array).getElementType()) + then result = ".ArrayElement" + else + if t instanceof ContainerType or t instanceof IterableClass + then result = ".Element" + else result = "" +} + +/** + * Gets the access path for parameter `p`. + */ +private string parameterAccess(Parameter p) { + result = "Argument[" + p.getPosition() + "]" + getAccessPath(p.getType()) +} + +/** + * Holds if `callable` has a type parameter `tv` or container parameterized over type `tv`. + */ +private predicate parameter(Callable callable, string input, TypeVariable tv) { + exists(Parameter p, Type pt | + input = parameterAccess(p) and + p = callable.getAParameter() and + pt = p.getType() and + ( + // Parameter of type tv + isEffectivelyUpperBound(pt, tv) + or + // Parameter is a container of type tv + genericContainerType(pt, tv) + ) + ) +} + +/** + * Gets the string representation of a synthetic field corresponding to `tv`. + */ +private string getSyntheticField(TypeVariable tv) { + result = ".SyntheticField[ArgType" + tv.getIndex() + "]" +} + +/** + * Gets a models as data string representation of, how a value of type `tv` + * can be read or stored implicitly in relation to `callable`. + */ +private string implicit(Callable callable, TypeVariable tv) { + classTypeParameter(callable, tv) and + not callable.isStatic() and + exists(string access, Type decl | + decl = callable.getDeclaringType() and + if genericContainerType(decl, tv) + then access = getAccessPath(decl) + else access = getSyntheticField(tv) + | + result = Specific::qualifierString() + access + ) +} + +private class GenericFunctionalInterface extends FunctionalInterface, GenericType { + override string getAPrimaryQlClass() { result = "GenericFunctionalInterface" } +} + +/** + * A class of types that represents functions. + */ +private class Function extends ParameterizedType { + private GenericFunctionalInterface fi; + + Function() { fi = this.getGenericType() } + + /** + * Gets the typevariable that is the placeholder for the type `t` + * used in the instantiation of `this`. + */ + private TypeVariable getTypeReplacement(Type t) { + exists(int position | + instantiates(this, fi, position, t) and + result = fi.getTypeParameter(position) + ) + } + + /** + * Gets the parameter type of `this` function at position `position`. + * Note that functions are often contravariant in their parameter types. + */ + Type getParameterType(int position) { + exists(Type t | + fi.getRunMethod().getParameterType(position) = this.getTypeReplacement(t) and + isEffectivelyLowerBound(t, result) + ) + } + + /** + * Gets the return type of `this` function. + * Note that functions are often covariant in their return type. + */ + Type getReturnType() { + exists(Type t | + fi.getRunMethod().getReturnType() = this.getTypeReplacement(t) and + isEffectivelyUpperBound(t, result) + ) + } +} + +/** + * Holds if `callable` has a function parameter `f` at parameter position `position`. + */ +private predicate functional(Callable callable, Function f, int position) { + callable.getParameterType(position) = f +} + +/** + * Gets models as data input/output access relative to the type parameter `tv` in the + * type `t` in the scope of `callable`. + * + * Note: This predicate has to be inlined as `callable` is not related to `return` or `tv` + * in every disjunction. + */ +bindingset[callable] +private string getAccess(Callable callable, Type return, TypeVariable tv) { + return = tv and result = "" + or + genericContainerType(return, tv) and result = getAccessPath(return) + or + not genericContainerType(return, tv) and + ( + return.(ParameterizedType).getATypeArgument() = tv + or + callable.getDeclaringType() = return and return.(GenericType).getATypeParameter() = tv + ) and + result = getSyntheticField(tv) +} + +/** + * Holds if `input` is a models as data string representation of, how a value of type `tv` + * (or a generic parameterized over `tv`) can be generated by a function parameter of `callable`. + */ +private predicate functionalSource(Callable callable, string input, TypeVariable tv) { + exists(Function f, int position, Type return, string access | + functional(callable, f, position) and + return = f.getReturnType() and + access = getAccess(callable, return, tv) and + input = "Argument[" + position + "].ReturnValue" + access + ) +} + +/** + * Holds if `input` is a models as data string representation of, how a + * value of type `tv` (or a generic parameterized over `tv`) + * can be provided as input to `callable`. + * This includes + * (1) The implicit synthetic field(s) of the declaring type of `callable`. + * (2) The parameters of `callable`. + * (3) Any function parameters of `callable`. + */ +private predicate input(Callable callable, string input, TypeVariable tv) { + input = implicit(callable, tv) + or + parameter(callable, input, tv) + or + functionalSource(callable, input, tv) +} + +/** + * Holds if `callable` returns a value of type `tv` (or a generic parameterized over `tv`) and `output` + * is a models as data string representation of, how data is routed to the return. + */ +private predicate returns(Callable callable, TypeVariable tv, string output) { + exists(Type return, string access | return = callable.getReturnType() | + access = getAccess(callable, return, tv) and + output = "ReturnValue" + access + ) +} + +/** + * Holds if `callable` has a function parameter that accepts a value of type `tv` + * and `output` is the models as data string representation of, how data is routed to + * the function parameter. + */ +private predicate functionalSink(Callable callable, TypeVariable tv, string output) { + exists(Function f, int p1, int p2 | + functional(callable, f, p1) and + tv = f.getParameterType(p2) and + output = "Argument[" + p1 + "]" + ".Parameter[" + p2 + "]" + ) +} + +/** + * Holds if `output` is a models as data string representation of, how values of type `tv` + * (or generics parameterized over `tv`) can be routed. + * This includes + * (1) The implicit synthetic field(s) of the declaring type of `callable`. + * (2) The return of `callable`. + * (3) Any function parameters of `callable`. + */ +private predicate output(Callable callable, TypeVariable tv, string output) { + output = implicit(callable, tv) + or + returns(callable, tv, output) + or + functionalSink(callable, tv, output) +} + +/** + * A class of callables that are relevant generating summaries for based + * on the Theorems for Free approach. + */ +class TypeBasedFlowTargetApi extends Specific::TargetApiSpecific { + TypeBasedFlowTargetApi() { Specific::isRelevantForTypeBasedFlowModels(this) } + + /** + * Gets the string representation of all type based summaries for `this` + * inspired by the Theorems for Free approach. + * + * Examples could be (see Java pseudo code below) + * (1) `get` returns a value of type `T`. We assume that the returned + * value was fetched from a (synthetic) field. + * (2) `set` consumes a value of type `T`. We assume that the value is stored in + * a (synthetic) field. + * (3) `apply` is assumed to apply the provided function to a value stored in + * a (synthetic) field and return the result. + * (4) `apply` is assumed to apply the provided function to provided value + * and return the result. + * ```java + * public class MyGeneric { + * public void set(T x) { ... } + * public T get() { ... } + * public S apply(Function f) { ... } + * public S2 apply(S1 x, Function f) { ... } + * } + * ``` + */ + string getSummaries() { + exists(TypeVariable tv, string input, string output | + localTypeParameter(this, tv) and + input(this, input, tv) and + output(this, tv, output) and + input != output + | + result = asValueModel(this, input, output) + ) + } +} + +/** + * Returns the Theorems for Free inspired typed based summaries for `api`. + */ +string captureFlow(TypeBasedFlowTargetApi api) { result = api.getSummaries() } diff --git a/java/ql/test/library-tests/regex/parser/RegexParseTests.ql b/java/ql/test/library-tests/regex/parser/RegexParseTests.ql index 345031a3b2d..4c8d7519f14 100644 --- a/java/ql/test/library-tests/regex/parser/RegexParseTests.ql +++ b/java/ql/test/library-tests/regex/parser/RegexParseTests.ql @@ -1,10 +1,12 @@ import java -import semmle.code.java.regex.RegexTreeView -import semmle.code.java.regex.regex +import semmle.code.java.regex.RegexTreeView as RegexTreeView +import semmle.code.java.regex.regex as Regex -string getQLClases(RegExpTerm t) { result = "[" + strictconcat(t.getPrimaryQLClass(), ",") + "]" } +string getQLClases(RegexTreeView::RegExpTerm t) { + result = "[" + strictconcat(t.getPrimaryQLClass(), ",") + "]" +} -query predicate parseFailures(Regex r, int i) { r.failedToParse(i) } +query predicate parseFailures(Regex::Regex r, int i) { r.failedToParse(i) } -from RegExpTerm t +from RegexTreeView::RegExpTerm t select t, getQLClases(t) diff --git a/java/ql/test/query-tests/security/CWE-730/PolynomialReDoS.ql b/java/ql/test/query-tests/security/CWE-730/PolynomialReDoS.ql index bd600a6d8af..c8c1566a7a4 100644 --- a/java/ql/test/query-tests/security/CWE-730/PolynomialReDoS.ql +++ b/java/ql/test/query-tests/security/CWE-730/PolynomialReDoS.ql @@ -1,4 +1,3 @@ -import java import TestUtilities.InlineExpectationsTest import semmle.code.java.security.regexp.PolynomialReDoSQuery @@ -9,7 +8,10 @@ class HasPolyRedos extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { tag = "hasPolyRedos" and - exists(DataFlow::PathNode source, DataFlow::PathNode sink, PolynomialBackTrackingTerm regexp | + exists( + DataFlow::PathNode source, DataFlow::PathNode sink, + SuperlinearBackTracking::PolynomialBackTrackingTerm regexp + | hasPolynomialReDoSResult(source, sink, regexp) and location = sink.getNode().getLocation() and element = sink.getNode().toString() and diff --git a/java/ql/test/query-tests/security/CWE-730/ReDoS.ql b/java/ql/test/query-tests/security/CWE-730/ReDoS.ql index 288ca57f2e2..7226541bcb2 100644 --- a/java/ql/test/query-tests/security/CWE-730/ReDoS.ql +++ b/java/ql/test/query-tests/security/CWE-730/ReDoS.ql @@ -1,6 +1,7 @@ import java import TestUtilities.InlineExpectationsTest -import semmle.code.java.security.regexp.ExponentialBackTracking +private import semmle.code.java.regex.RegexTreeView::RegexTreeView as TreeView +import codeql.regex.nfa.ExponentialBackTracking::Make as ExponentialBackTracking import semmle.code.java.regex.regex class HasExpRedos extends InlineExpectationsTest { @@ -10,8 +11,8 @@ class HasExpRedos extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { tag = "hasExpRedos" and - exists(RegExpTerm t, string pump, State s, string prefixMsg | - hasReDoSResult(t, pump, s, prefixMsg) and + exists(TreeView::RegExpTerm t, string pump, ExponentialBackTracking::State s, string prefixMsg | + ExponentialBackTracking::hasReDoSResult(t, pump, s, prefixMsg) and not t.getRegex().getAMode() = "VERBOSE" and value = "" and location = t.getLocation() and diff --git a/java/ql/test/utils/model-generator/CaptureNegativeSummaryModels.expected b/java/ql/test/utils/model-generator/dataflow/CaptureNegativeSummaryModels.expected similarity index 100% rename from java/ql/test/utils/model-generator/CaptureNegativeSummaryModels.expected rename to java/ql/test/utils/model-generator/dataflow/CaptureNegativeSummaryModels.expected diff --git a/java/ql/test/utils/model-generator/CaptureNegativeSummaryModels.qlref b/java/ql/test/utils/model-generator/dataflow/CaptureNegativeSummaryModels.qlref similarity index 100% rename from java/ql/test/utils/model-generator/CaptureNegativeSummaryModels.qlref rename to java/ql/test/utils/model-generator/dataflow/CaptureNegativeSummaryModels.qlref diff --git a/java/ql/test/utils/model-generator/CaptureSinkModels.expected b/java/ql/test/utils/model-generator/dataflow/CaptureSinkModels.expected similarity index 100% rename from java/ql/test/utils/model-generator/CaptureSinkModels.expected rename to java/ql/test/utils/model-generator/dataflow/CaptureSinkModels.expected diff --git a/java/ql/test/utils/model-generator/CaptureSinkModels.qlref b/java/ql/test/utils/model-generator/dataflow/CaptureSinkModels.qlref similarity index 100% rename from java/ql/test/utils/model-generator/CaptureSinkModels.qlref rename to java/ql/test/utils/model-generator/dataflow/CaptureSinkModels.qlref diff --git a/java/ql/test/utils/model-generator/CaptureSourceModels.expected b/java/ql/test/utils/model-generator/dataflow/CaptureSourceModels.expected similarity index 100% rename from java/ql/test/utils/model-generator/CaptureSourceModels.expected rename to java/ql/test/utils/model-generator/dataflow/CaptureSourceModels.expected diff --git a/java/ql/test/utils/model-generator/CaptureSourceModels.qlref b/java/ql/test/utils/model-generator/dataflow/CaptureSourceModels.qlref similarity index 100% rename from java/ql/test/utils/model-generator/CaptureSourceModels.qlref rename to java/ql/test/utils/model-generator/dataflow/CaptureSourceModels.qlref diff --git a/java/ql/test/utils/model-generator/CaptureSummaryModels.expected b/java/ql/test/utils/model-generator/dataflow/CaptureSummaryModels.expected similarity index 100% rename from java/ql/test/utils/model-generator/CaptureSummaryModels.expected rename to java/ql/test/utils/model-generator/dataflow/CaptureSummaryModels.expected diff --git a/java/ql/test/utils/model-generator/CaptureSummaryModels.qlref b/java/ql/test/utils/model-generator/dataflow/CaptureSummaryModels.qlref similarity index 100% rename from java/ql/test/utils/model-generator/CaptureSummaryModels.qlref rename to java/ql/test/utils/model-generator/dataflow/CaptureSummaryModels.qlref diff --git a/java/ql/test/utils/model-generator/p/AbstractImplOfExternalSPI.java b/java/ql/test/utils/model-generator/dataflow/p/AbstractImplOfExternalSPI.java similarity index 100% rename from java/ql/test/utils/model-generator/p/AbstractImplOfExternalSPI.java rename to java/ql/test/utils/model-generator/dataflow/p/AbstractImplOfExternalSPI.java diff --git a/java/ql/test/utils/model-generator/p/Factory.java b/java/ql/test/utils/model-generator/dataflow/p/Factory.java similarity index 100% rename from java/ql/test/utils/model-generator/p/Factory.java rename to java/ql/test/utils/model-generator/dataflow/p/Factory.java diff --git a/java/ql/test/utils/model-generator/p/FinalClass.java b/java/ql/test/utils/model-generator/dataflow/p/FinalClass.java similarity index 100% rename from java/ql/test/utils/model-generator/p/FinalClass.java rename to java/ql/test/utils/model-generator/dataflow/p/FinalClass.java diff --git a/java/ql/test/utils/model-generator/p/FluentAPI.java b/java/ql/test/utils/model-generator/dataflow/p/FluentAPI.java similarity index 100% rename from java/ql/test/utils/model-generator/p/FluentAPI.java rename to java/ql/test/utils/model-generator/dataflow/p/FluentAPI.java diff --git a/java/ql/test/utils/model-generator/p/ImmutablePojo.java b/java/ql/test/utils/model-generator/dataflow/p/ImmutablePojo.java similarity index 100% rename from java/ql/test/utils/model-generator/p/ImmutablePojo.java rename to java/ql/test/utils/model-generator/dataflow/p/ImmutablePojo.java diff --git a/java/ql/test/utils/model-generator/p/ImplOfExternalSPI.java b/java/ql/test/utils/model-generator/dataflow/p/ImplOfExternalSPI.java similarity index 100% rename from java/ql/test/utils/model-generator/p/ImplOfExternalSPI.java rename to java/ql/test/utils/model-generator/dataflow/p/ImplOfExternalSPI.java diff --git a/java/ql/test/utils/model-generator/p/InnerClasses.java b/java/ql/test/utils/model-generator/dataflow/p/InnerClasses.java similarity index 100% rename from java/ql/test/utils/model-generator/p/InnerClasses.java rename to java/ql/test/utils/model-generator/dataflow/p/InnerClasses.java diff --git a/java/ql/test/utils/model-generator/p/InnerHolder.java b/java/ql/test/utils/model-generator/dataflow/p/InnerHolder.java similarity index 100% rename from java/ql/test/utils/model-generator/p/InnerHolder.java rename to java/ql/test/utils/model-generator/dataflow/p/InnerHolder.java diff --git a/java/ql/test/utils/model-generator/p/Joiner.java b/java/ql/test/utils/model-generator/dataflow/p/Joiner.java similarity index 100% rename from java/ql/test/utils/model-generator/p/Joiner.java rename to java/ql/test/utils/model-generator/dataflow/p/Joiner.java diff --git a/java/ql/test/utils/model-generator/p/MultipleImpls.java b/java/ql/test/utils/model-generator/dataflow/p/MultipleImpls.java similarity index 100% rename from java/ql/test/utils/model-generator/p/MultipleImpls.java rename to java/ql/test/utils/model-generator/dataflow/p/MultipleImpls.java diff --git a/java/ql/test/utils/model-generator/p/ParamFlow.java b/java/ql/test/utils/model-generator/dataflow/p/ParamFlow.java similarity index 100% rename from java/ql/test/utils/model-generator/p/ParamFlow.java rename to java/ql/test/utils/model-generator/dataflow/p/ParamFlow.java diff --git a/java/ql/test/utils/model-generator/p/Pojo.java b/java/ql/test/utils/model-generator/dataflow/p/Pojo.java similarity index 100% rename from java/ql/test/utils/model-generator/p/Pojo.java rename to java/ql/test/utils/model-generator/dataflow/p/Pojo.java diff --git a/java/ql/test/utils/model-generator/p/PrivateFlowViaPublicInterface.java b/java/ql/test/utils/model-generator/dataflow/p/PrivateFlowViaPublicInterface.java similarity index 100% rename from java/ql/test/utils/model-generator/p/PrivateFlowViaPublicInterface.java rename to java/ql/test/utils/model-generator/dataflow/p/PrivateFlowViaPublicInterface.java diff --git a/java/ql/test/utils/model-generator/p/Sinks.java b/java/ql/test/utils/model-generator/dataflow/p/Sinks.java similarity index 100% rename from java/ql/test/utils/model-generator/p/Sinks.java rename to java/ql/test/utils/model-generator/dataflow/p/Sinks.java diff --git a/java/ql/test/utils/model-generator/p/SomeEnum.java b/java/ql/test/utils/model-generator/dataflow/p/SomeEnum.java similarity index 100% rename from java/ql/test/utils/model-generator/p/SomeEnum.java rename to java/ql/test/utils/model-generator/dataflow/p/SomeEnum.java diff --git a/java/ql/test/utils/model-generator/p/Sources.java b/java/ql/test/utils/model-generator/dataflow/p/Sources.java similarity index 100% rename from java/ql/test/utils/model-generator/p/Sources.java rename to java/ql/test/utils/model-generator/dataflow/p/Sources.java diff --git a/java/ql/test/utils/model-generator/typebasedflow/CaptureTypeBasedSummaryModels.expected b/java/ql/test/utils/model-generator/typebasedflow/CaptureTypeBasedSummaryModels.expected new file mode 100644 index 00000000000..ee55a9c6ba6 --- /dev/null +++ b/java/ql/test/utils/model-generator/typebasedflow/CaptureTypeBasedSummaryModels.expected @@ -0,0 +1,2 @@ +unexpectedSummary +expectedSummary diff --git a/java/ql/test/utils/model-generator/typebasedflow/CaptureTypeBasedSummaryModels.ql b/java/ql/test/utils/model-generator/typebasedflow/CaptureTypeBasedSummaryModels.ql new file mode 100644 index 00000000000..d1f5c9c520e --- /dev/null +++ b/java/ql/test/utils/model-generator/typebasedflow/CaptureTypeBasedSummaryModels.ql @@ -0,0 +1,26 @@ +import java +import utils.modelgenerator.internal.CaptureTypeBasedSummaryModels + +private string expects() { + exists(Javadoc doc | + doc.getChild(0).toString().regexpCapture(" *(SPURIOUS-)?MaD=(.*)", 2) = result + ) +} + +private string flows() { exists(TypeBasedFlowTargetApi api | result = captureFlow(api)) } + +query predicate unexpectedSummary(string msg) { + exists(string flow | + flow = flows() and + not flow = expects() and + msg = "Unexpected summary found: " + flow + ) +} + +query predicate expectedSummary(string msg) { + exists(string e | + e = expects() and + not e = flows() and + msg = "Expected summary missing: " + e + ) +} diff --git a/java/ql/test/utils/model-generator/typebasedflow/p/MyFunction.java b/java/ql/test/utils/model-generator/typebasedflow/p/MyFunction.java new file mode 100644 index 00000000000..2dea243bf98 --- /dev/null +++ b/java/ql/test/utils/model-generator/typebasedflow/p/MyFunction.java @@ -0,0 +1,10 @@ +package p; + +@FunctionalInterface +public interface MyFunction { + + // MaD=p;MyFunction;true;apply;(Object,Object);;Argument[-1].SyntheticField[ArgType2];ReturnValue;value;generated + // MaD=p;MyFunction;true;apply;(Object,Object);;Argument[0];Argument[-1].SyntheticField[ArgType0];value;generated + // MaD=p;MyFunction;true;apply;(Object,Object);;Argument[1];Argument[-1].SyntheticField[ArgType1];value;generated + T3 apply(T1 x, T2 y); +} \ No newline at end of file diff --git a/java/ql/test/utils/model-generator/typebasedflow/p/Stream.java b/java/ql/test/utils/model-generator/typebasedflow/p/Stream.java new file mode 100644 index 00000000000..c148df6a91b --- /dev/null +++ b/java/ql/test/utils/model-generator/typebasedflow/p/Stream.java @@ -0,0 +1,247 @@ +package p; + +import java.util.*; +import java.util.function.*; +import java.util.stream.LongStream; +import java.util.stream.IntStream; +import java.util.stream.DoubleStream; +import java.util.stream.Collector; + +/** + * This is a stub implementation of the Java Stream API. + */ +public class Stream { + + // MaD=p;Stream;true;iterator;();;Argument[-1].Element;ReturnValue.Element;value;generated + public Iterator iterator() { + return null; + } + + // MaD=p;Stream;true;allMatch;(Predicate);;Argument[-1].Element;Argument[0].Parameter[0];value;generated + public boolean allMatch(Predicate predicate) { + return false; + } + + // MaD=p;Stream;true;collect;(Supplier,BiConsumer,BiConsumer);;Argument[-1].Element;Argument[1].Parameter[1];value;generated + // MaD=p;Stream;true;collect;(Supplier,BiConsumer,BiConsumer);;Argument[0].ReturnValue;Argument[1].Parameter[0];value;generated + // MaD=p;Stream;true;collect;(Supplier,BiConsumer,BiConsumer);;Argument[0].ReturnValue;Argument[2].Parameter[0];value;generated + // MaD=p;Stream;true;collect;(Supplier,BiConsumer,BiConsumer);;Argument[0].ReturnValue;Argument[2].Parameter[1];value;generated + // MaD=p;Stream;true;collect;(Supplier,BiConsumer,BiConsumer);;Argument[0].ReturnValue;ReturnValue;value;generated + public R collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner) { + return null; + } + + // Collector is not a functional interface, so this is not supported + public R collect(Collector collector) { + return null; + } + + // MaD=p;Stream;true;concat;(Stream,Stream);;Argument[0].Element;ReturnValue.Element;value;generated + // MaD=p;Stream;true;concat;(Stream,Stream);;Argument[1].Element;ReturnValue.Element;value;generated + public static Stream concat(Stream a, Stream b) { + return null; + } + + // MaD=p;Stream;true;distinct;();;Argument[-1].Element;ReturnValue.Element;value;generated + public Stream distinct() { + return null; + } + + public static Stream empty() { + return null; + } + + // MaD=p;Stream;true;filter;(Predicate);;Argument[-1].Element;Argument[0].Parameter[0];value;generated + // MaD=p;Stream;true;filter;(Predicate);;Argument[-1].Element;ReturnValue.Element;value;generated + public Stream filter(Predicate predicate) { + return null; + } + + // MaD=p;Stream;true;findAny;();;Argument[-1].Element;ReturnValue.SyntheticField[ArgType0];value;generated + public Optional findAny() { + return null; + } + + // MaD=p;Stream;true;findFirst;();;Argument[-1].Element;ReturnValue.SyntheticField[ArgType0];value;generated + public Optional findFirst() { + return null; + } + + // MaD=p;Stream;true;flatMap;(Function);;Argument[0].ReturnValue.Element;ReturnValue.Element;value;generated + // MaD=p;Stream;true;flatMap;(Function);;Argument[-1].Element;Argument[0].Parameter[0];value;generated + public Stream flatMap(Function> mapper) { + return null; + } + + // MaD=p;Stream;true;flatMapToDouble;(Function);;Argument[-1].Element;Argument[0].Parameter[0];value;generated + public DoubleStream flatMapToDouble(Function mapper) { + return null; + } + + // MaD=p;Stream;true;flatMapToInt;(Function);;Argument[-1].Element;Argument[0].Parameter[0];value;generated + public IntStream flatMapToInt(Function mapper) { + return null; + } + + // MaD=p;Stream;true;flatMapToLong;(Function);;Argument[-1].Element;Argument[0].Parameter[0];value;generated + public LongStream flatMapToLong(Function mapper) { + return null; + } + + // MaD=p;Stream;true;forEach;(Consumer);;Argument[-1].Element;Argument[0].Parameter[0];value;generated + public void forEach(Consumer action) { + } + + // MaD=p;Stream;true;forEachOrdered;(Consumer);;Argument[-1].Element;Argument[0].Parameter[0];value;generated + public void forEachOrdered(Consumer action) { + } + + // MaD=p;Stream;true;generate;(Supplier);;Argument[0].ReturnValue;ReturnValue.Element;value;generated + public static Stream generate(Supplier s) { + return null; + } + + // MaD=p;Stream;true;iterate;(Object,UnaryOperator);;Argument[0];Argument[1].Parameter[0];value;generated + // MaD=p;Stream;true;iterate;(Object,UnaryOperator);;Argument[0];ReturnValue.Element;value;generated + // MaD=p;Stream;true;iterate;(Object,UnaryOperator);;Argument[1].ReturnValue;Argument[1].Parameter[0];value;generated + // MaD=p;Stream;true;iterate;(Object,UnaryOperator);;Argument[1].ReturnValue;ReturnValue.Element;value;generated + public static Stream iterate(T seed, UnaryOperator f) { + return null; + } + + // MaD=p;Stream;true;limit;(long);;Argument[-1].Element;ReturnValue.Element;value;generated + public Stream limit(long maxSize) { + return null; + } + + // MaD=p;Stream;true;map;(Function);;Argument[-1].Element;Argument[0].Parameter[0];value;generated + // MaD=p;Stream;true;map;(Function);;Argument[0].ReturnValue;ReturnValue.Element;value;generated + public Stream map(Function mapper) { + return null; + } + + // MaD=p;Stream;true;mapToDouble;(ToDoubleFunction);;Argument[-1].Element;Argument[0].Parameter[0];value;generated + public DoubleStream mapToDouble(ToDoubleFunction mapper) { + return null; + } + + // MaD=p;Stream;true;mapToInt;(ToIntFunction);;Argument[-1].Element;Argument[0].Parameter[0];value;generated + public IntStream mapToInt(ToIntFunction mapper) { + return null; + } + + // MaD=p;Stream;true;mapToLong;(ToLongFunction);;Argument[-1].Element;Argument[0].Parameter[0];value;generated + public LongStream mapToLong(ToLongFunction mapper) { + return null; + } + + // MaD=p;Stream;true;max;(Comparator);;Argument[-1].Element;Argument[0].Parameter[0];value;generated + // MaD=p;Stream;true;max;(Comparator);;Argument[-1].Element;Argument[0].Parameter[1];value;generated + // MaD=p;Stream;true;max;(Comparator);;Argument[-1].Element;ReturnValue.SyntheticField[ArgType0];value;generated + public Optional max(Comparator comparator) { + return null; + } + + // MaD=p;Stream;true;min;(Comparator);;Argument[-1].Element;Argument[0].Parameter[0];value;generated + // MaD=p;Stream;true;min;(Comparator);;Argument[-1].Element;Argument[0].Parameter[1];value;generated + // MaD=p;Stream;true;min;(Comparator);;Argument[-1].Element;ReturnValue.SyntheticField[ArgType0];value;generated + public Optional min(Comparator comparator) { + return null; + } + + // MaD=p;Stream;true;noneMatch;(Predicate);;Argument[-1].Element;Argument[0].Parameter[0];value;generated + public boolean noneMatch(Predicate predicate) { + return false; + } + + // MaD=p;Stream;true;of;(Object[]);;Argument[0].ArrayElement;ReturnValue.Element;value;generated + public static Stream of(T... t) { + return null; + } + + // MaD=p;Stream;true;of;(Object);;Argument[0];ReturnValue.Element;value;generated + public static Stream of(T t) { + return null; + } + + // MaD=p;Stream;true;peek;(Consumer);;Argument[-1].Element;Argument[0].Parameter[0];value;generated + // MaD=p;Stream;true;peek;(Consumer);;Argument[-1].Element;ReturnValue.Element;value;generated + public Stream peek(Consumer action) { + return null; + } + + // The generated models are only partially correct. + // MaD=p;Stream;true;reduce;(BinaryOperator);;Argument[-1].Element;Argument[0].Parameter[0];value;generated + // MaD=p;Stream;true;reduce;(BinaryOperator);;Argument[-1].Element;Argument[0].Parameter[1];value;generated + // MaD=p;Stream;true;reduce;(BinaryOperator);;Argument[-1].Element;ReturnValue.SyntheticField[ArgType0];value;generated + // MaD=p;Stream;true;reduce;(BinaryOperator);;Argument[0].ReturnValue;Argument[0].Parameter[0];value;generated + // MaD=p;Stream;true;reduce;(BinaryOperator);;Argument[0].ReturnValue;Argument[0].Parameter[1];value;generated + // MaD=p;Stream;true;reduce;(BinaryOperator);;Argument[0].ReturnValue;ReturnValue.SyntheticField[ArgType0];value;generated + // SPURIOUS-MaD=p;Stream;true;reduce;(BinaryOperator);;Argument[0].ReturnValue;Argument[-1].Element;value;generated + public Optional reduce(BinaryOperator accumulator) { + return null; + } + + // The generated models are only partially correct. + // MaD=p;Stream;true;reduce;(Object,BinaryOperator);;Argument[-1].Element;Argument[1].Parameter[0];value;generated + // MaD=p;Stream;true;reduce;(Object,BinaryOperator);;Argument[-1].Element;Argument[1].Parameter[1];value;generated + // MaD=p;Stream;true;reduce;(Object,BinaryOperator);;Argument[0];Argument[1].Parameter[0];value;generated + // MaD=p;Stream;true;reduce;(Object,BinaryOperator);;Argument[0];Argument[1].Parameter[1];value;generated + // MaD=p;Stream;true;reduce;(Object,BinaryOperator);;Argument[0];ReturnValue;value;generated + // MaD=p;Stream;true;reduce;(Object,BinaryOperator);;Argument[1].ReturnValue;Argument[1].Parameter[0];value;generated + // MaD=p;Stream;true;reduce;(Object,BinaryOperator);;Argument[1].ReturnValue;Argument[1].Parameter[1];value;generated + // MaD=p;Stream;true;reduce;(Object,BinaryOperator);;Argument[1].ReturnValue;ReturnValue;value;generated + // SPURIOUS-MaD=p;Stream;true;reduce;(Object,BinaryOperator);;Argument[-1].Element;ReturnValue;value;generated + // SPURIOUS-MaD=p;Stream;true;reduce;(Object,BinaryOperator);;Argument[0];Argument[-1].Element;value;generated + // SPURIOUS-MaD=p;Stream;true;reduce;(Object,BinaryOperator);;Argument[1].ReturnValue;Argument[-1].Element;value;generated + public T reduce(T identity, BinaryOperator accumulator) { + return null; + } + + // MaD=p;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;Argument[-1].Element;Argument[1].Parameter[1];value;generated + // MaD=p;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;Argument[0];Argument[1].Parameter[0];value;generated + // MaD=p;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;Argument[0];Argument[2].Parameter[0];value;generated + // MaD=p;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;Argument[0];Argument[2].Parameter[1];value;generated + // MaD=p;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;Argument[0];ReturnValue;value;generated + // MaD=p;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;Argument[1].ReturnValue;Argument[1].Parameter[0];value;generated + // MaD=p;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;Argument[1].ReturnValue;Argument[2].Parameter[0];value;generated + // MaD=p;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;Argument[1].ReturnValue;Argument[2].Parameter[1];value;generated + // MaD=p;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;Argument[1].ReturnValue;ReturnValue;value;generated + // MaD=p;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;Argument[2].ReturnValue;Argument[1].Parameter[0];value;generated + // MaD=p;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;Argument[2].ReturnValue;Argument[2].Parameter[0];value;generated + // MaD=p;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;Argument[2].ReturnValue;Argument[2].Parameter[1];value;generated + // MaD=p;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;Argument[2].ReturnValue;ReturnValue;value;generated + public U reduce(U identity, BiFunction accumulator, BinaryOperator combiner) { + return null; + } + + // MaD=p;Stream;true;skip;(long);;Argument[-1].Element;ReturnValue.Element;value;generated + public Stream skip(long n) { + return null; + } + + // MaD=p;Stream;true;sorted;();;Argument[-1].Element;ReturnValue.Element;value;generated + public Stream sorted() { + return null; + } + + // MaD=p;Stream;true;sorted;(Comparator);;Argument[-1].Element;Argument[0].Parameter[0];value;generated + // MaD=p;Stream;true;sorted;(Comparator);;Argument[-1].Element;Argument[0].Parameter[1];value;generated + // MaD=p;Stream;true;sorted;(Comparator);;Argument[-1].Element;ReturnValue.Element;value;generated + public Stream sorted(Comparator comparator) { + return null; + } + + // Models can never be generated correctly based on the type information + // as it involves downcasting. + public Object[] toArray() { + return null; + } + + // The generated result is only partially correct as there is no mentioning of + // the type T in the method definition. + // MaD=p;Stream;true;toArray;(IntFunction);;Argument[0].ReturnValue.ArrayElement;ReturnValue.ArrayElement;value;generated + public A[] toArray(IntFunction generator) { + return null; + } +} \ No newline at end of file diff --git a/java/ql/test/utils/model-generator/typebasedflow/p/TypeBasedCollection.java b/java/ql/test/utils/model-generator/typebasedflow/p/TypeBasedCollection.java new file mode 100644 index 00000000000..6cd7dcdc5af --- /dev/null +++ b/java/ql/test/utils/model-generator/typebasedflow/p/TypeBasedCollection.java @@ -0,0 +1,25 @@ +package p; + +import java.util.List; +import java.util.ArrayList; + +public class TypeBasedCollection extends ArrayList { + + // MaD=p;TypeBasedCollection;true;addT;(Object);;Argument[0];Argument[-1].Element;value;generated + public void addT(T x) { + } + + // MaD=p;TypeBasedCollection;true;addManyT;(List);;Argument[0].Element;Argument[-1].Element;value;generated + public void addManyT(List xs) { + } + + // MaD=p;TypeBasedCollection;true;firstT;();;Argument[-1].Element;ReturnValue;value;generated + public T firstT() { + return null; + } + + // MaD=p;TypeBasedCollection;true;getManyT;();;Argument[-1].Element;ReturnValue.Element;value;generated + public List getManyT() { + return null; + } +} \ No newline at end of file diff --git a/java/ql/test/utils/model-generator/typebasedflow/p/TypeBasedComplex.java b/java/ql/test/utils/model-generator/typebasedflow/p/TypeBasedComplex.java new file mode 100644 index 00000000000..127f319b49b --- /dev/null +++ b/java/ql/test/utils/model-generator/typebasedflow/p/TypeBasedComplex.java @@ -0,0 +1,91 @@ +package p; + +import java.util.List; +import java.util.function.Function; + +public class TypeBasedComplex { + + // MaD=p;TypeBasedComplex;true;addMany;(List);;Argument[0].Element;Argument[-1].SyntheticField[ArgType0];value;generated + public void addMany(List xs) { + } + + // MaD=p;TypeBasedComplex;true;getMany;();;Argument[-1].SyntheticField[ArgType0];ReturnValue.Element;value;generated + public List getMany() { + return null; + } + + // MaD=p;TypeBasedComplex;true;apply;(Function);;Argument[-1].SyntheticField[ArgType0];Argument[0].Parameter[0];value;generated + public Integer apply(Function f) { + return null; + } + + // A method that doesn't mention `T` in its type signature. + // This is for testing that we don't generate a summary that involves the + // implicit field for `T`. + // MaD=p;TypeBasedComplex;true;apply2;(Object,Function);;Argument[0];Argument[1].Parameter[0];value;generated + // MaD=p;TypeBasedComplex;true;apply2;(Object,Function);;Argument[1].ReturnValue;ReturnValue;value;generated + public T2 apply2(T1 x, Function f) { + return null; + } + + // MaD=p;TypeBasedComplex;true;flatMap;(Function);;Argument[-1].SyntheticField[ArgType0];Argument[0].Parameter[0];value;generated + // MaD=p;TypeBasedComplex;true;flatMap;(Function);;Argument[-1].SyntheticField[ArgType0];ReturnValue.SyntheticField[ArgType0];value;generated + // MaD=p;TypeBasedComplex;true;flatMap;(Function);;Argument[0].ReturnValue.Element;Argument[-1].SyntheticField[ArgType0];value;generated + // MaD=p;TypeBasedComplex;true;flatMap;(Function);;Argument[0].ReturnValue.Element;Argument[0].Parameter[0];value;generated + // MaD=p;TypeBasedComplex;true;flatMap;(Function);;Argument[0].ReturnValue.Element;ReturnValue.SyntheticField[ArgType0];value;generated + public TypeBasedComplex flatMap(Function> f) { + return null; + } + + // MaD=p;TypeBasedComplex;true;flatMap2;(Function);;Argument[-1].SyntheticField[ArgType0];Argument[0].Parameter[0];value;generated + // MaD=p;TypeBasedComplex;true;flatMap2;(Function);;Argument[0].ReturnValue.Element;ReturnValue.SyntheticField[ArgType0];value;generated + public TypeBasedComplex flatMap2(Function> f) { + return null; + } + + // MaD=p;TypeBasedComplex;true;map;(Function);;Argument[-1].SyntheticField[ArgType0];Argument[0].Parameter[0];value;generated + // MaD=p;TypeBasedComplex;true;map;(Function);;Argument[0].ReturnValue;ReturnValue;value;generated + public S map(Function f) { + return null; + } + + // MaD=p;TypeBasedComplex;true;mapComplex;(Function);;Argument[-1].SyntheticField[ArgType0];Argument[0].Parameter[0];value;generated + // MaD=p;TypeBasedComplex;true;mapComplex;(Function);;Argument[0].ReturnValue;ReturnValue.SyntheticField[ArgType0];value;generated + public TypeBasedComplex mapComplex(Function f) { + return null; + } + + // MaD=p;TypeBasedComplex;true;returnComplex;(Function);;Argument[-1].SyntheticField[ArgType0];Argument[0].Parameter[0];value;generated + // MaD=p;TypeBasedComplex;true;returnComplex;(Function);;Argument[-1].SyntheticField[ArgType0];ReturnValue.SyntheticField[ArgType0];value;generated + // MaD=p;TypeBasedComplex;true;returnComplex;(Function);;Argument[0].ReturnValue.SyntheticField[ArgType0];Argument[-1].SyntheticField[ArgType0];value;generated + // MaD=p;TypeBasedComplex;true;returnComplex;(Function);;Argument[0].ReturnValue.SyntheticField[ArgType0];Argument[0].Parameter[0];value;generated + // MaD=p;TypeBasedComplex;true;returnComplex;(Function);;Argument[0].ReturnValue.SyntheticField[ArgType0];ReturnValue.SyntheticField[ArgType0];value;generated + public TypeBasedComplex returnComplex(Function> f) { + return null; + } + + // MaD=p;TypeBasedComplex;true;set;(Integer,Function);;Argument[1].ReturnValue;Argument[-1].SyntheticField[ArgType0];value;generated + public void set(Integer x, Function f) { + } + + // MaD=p;TypeBasedComplex;true;applyMyFunction;(MyFunction,Integer);;Argument[-1].SyntheticField[ArgType0];Argument[0].Parameter[0];value;generated + // MaD=p;TypeBasedComplex;true;applyMyFunction;(MyFunction,Integer);;Argument[0].ReturnValue;Argument[-1].SyntheticField[ArgType0];value;generated + // MaD=p;TypeBasedComplex;true;applyMyFunction;(MyFunction,Integer);;Argument[0].ReturnValue;Argument[0].Parameter[0];value;generated + public Integer applyMyFunction(MyFunction f, Integer x) { + return null; + } + + // MaD=p;TypeBasedComplex;true;applyMyFunctionGeneric;(MyFunction,Object);;Argument[-1].SyntheticField[ArgType0];Argument[0].Parameter[0];value;generated + // MaD=p;TypeBasedComplex;true;applyMyFunctionGeneric;(MyFunction,Object);;Argument[0].ReturnValue;ReturnValue;value;generated + // MaD=p;TypeBasedComplex;true;applyMyFunctionGeneric;(MyFunction,Object);;Argument[1];Argument[0].Parameter[1];value;generated + public S2 applyMyFunctionGeneric(MyFunction f, S1 x) { + return null; + } + + // MaD=p;TypeBasedComplex;true;applyMyFunctionGeneric;(MyFunction,Object,Object);;Argument[0].ReturnValue;ReturnValue;value;generated + // MaD=p;TypeBasedComplex;true;applyMyFunctionGeneric;(MyFunction,Object,Object);;Argument[1];Argument[0].Parameter[0];value;generated + // MaD=p;TypeBasedComplex;true;applyMyFunctionGeneric;(MyFunction,Object,Object);;Argument[2];Argument[0].Parameter[1];value;generated + public S3 applyMyFunctionGeneric(MyFunction f, S1 x, S2 y) { + return null; + } +} \ No newline at end of file diff --git a/java/ql/test/utils/model-generator/typebasedflow/p/TypeBasedSimple.java b/java/ql/test/utils/model-generator/typebasedflow/p/TypeBasedSimple.java new file mode 100644 index 00000000000..59fe6e48f13 --- /dev/null +++ b/java/ql/test/utils/model-generator/typebasedflow/p/TypeBasedSimple.java @@ -0,0 +1,42 @@ +package p; + +public class TypeBasedSimple { + + // MaD=p;TypeBasedSimple;true;TypeBasedSimple;(Object);;Argument[0];Argument[-1].SyntheticField[ArgType0];value;generated + public TypeBasedSimple(T t) { + } + + // MaD=p;TypeBasedSimple;true;get;();;Argument[-1].SyntheticField[ArgType0];ReturnValue;value;generated + public T get() { + return null; + } + + // MaD=p;TypeBasedSimple;true;get;(Object);;Argument[-1].SyntheticField[ArgType0];ReturnValue;value;generated + public T get(Object o) { + return null; + } + + // MaD=p;TypeBasedSimple;true;id;(Object);;Argument[-1].SyntheticField[ArgType0];ReturnValue;value;generated + // MaD=p;TypeBasedSimple;true;id;(Object);;Argument[0];Argument[-1].SyntheticField[ArgType0];value;generated + // MaD=p;TypeBasedSimple;true;id;(Object);;Argument[0];ReturnValue;value;generated + public T id(T x) { + return null; + } + + // MaD=p;TypeBasedSimple;true;id2;(Object);;Argument[0];ReturnValue;value;generated + public S id2(S x) { + return null; + } + + // MaD=p;TypeBasedSimple;true;set;(Object);;Argument[0];Argument[-1].SyntheticField[ArgType0];value;generated + public void set(T x) { + } + + // MaD=p;TypeBasedSimple;true;set;(int,Object);;Argument[1];Argument[-1].SyntheticField[ArgType0];value;generated + public void set(int x, T y) { + } + + // No summary as S is unrelated to T + public void set2(S x) { + } +} \ No newline at end of file diff --git a/javascript/extractor/src/com/semmle/js/extractor/Main.java b/javascript/extractor/src/com/semmle/js/extractor/Main.java index a15072f2017..f03702ac10d 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/Main.java +++ b/javascript/extractor/src/com/semmle/js/extractor/Main.java @@ -41,7 +41,7 @@ public class Main { * A version identifier that should be updated every time the extractor changes in such a way that * it may produce different tuples for the same file under the same {@link ExtractorConfig}. */ - public static final String EXTRACTOR_VERSION = "2022-11-10"; + public static final String EXTRACTOR_VERSION = "2022-11-15"; public static final Pattern NEWLINE = Pattern.compile("\n"); diff --git a/javascript/extractor/src/com/semmle/js/extractor/TypeExprKinds.java b/javascript/extractor/src/com/semmle/js/extractor/TypeExprKinds.java index a1c7b219a8a..82d4e4319c8 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/TypeExprKinds.java +++ b/javascript/extractor/src/com/semmle/js/extractor/TypeExprKinds.java @@ -10,6 +10,7 @@ import com.semmle.js.ast.TemplateElement; import com.semmle.js.extractor.ASTExtractor.IdContext; import com.semmle.ts.ast.ArrayTypeExpr; import com.semmle.ts.ast.ConditionalTypeExpr; +import com.semmle.js.ast.DynamicImport; import com.semmle.ts.ast.FunctionTypeExpr; import com.semmle.ts.ast.GenericTypeExpr; import com.semmle.ts.ast.ImportTypeExpr; @@ -221,8 +222,7 @@ public class TypeExprKinds { return inferTypeExpr; } - @Override - public Integer visit(ImportTypeExpr nd, Void c) { + private Integer handleInlineImport() { switch (idcontext) { case NAMESPACE_BIND: return importNamespaceAccess; @@ -235,6 +235,17 @@ public class TypeExprKinds { } } + @Override + public Integer visit(ImportTypeExpr nd, Void c) { + return handleInlineImport(); + } + + @Override + public Integer visit(DynamicImport nd, Void c) { + // These may appear in interface 'extend' clauses + return handleInlineImport(); + } + @Override public Integer visit(OptionalTypeExpr nd, Void c) { return optionalTypeExpr; diff --git a/javascript/extractor/tests/ts/input/dynamic-type.ts b/javascript/extractor/tests/ts/input/dynamic-type.ts new file mode 100644 index 00000000000..2b2f90337a2 --- /dev/null +++ b/javascript/extractor/tests/ts/input/dynamic-type.ts @@ -0,0 +1 @@ +interface Foo extends import("foo").Bar {} diff --git a/javascript/extractor/tests/ts/output/trap/dynamic-type.ts.trap b/javascript/extractor/tests/ts/output/trap/dynamic-type.ts.trap new file mode 100644 index 00000000000..71410d093cc --- /dev/null +++ b/javascript/extractor/tests/ts/output/trap/dynamic-type.ts.trap @@ -0,0 +1,139 @@ +#10000=@"/dynamic-type.ts;sourcefile" +files(#10000,"/dynamic-type.ts") +#10001=@"/;folder" +folders(#10001,"/") +containerparent(#10001,#10000) +#10002=@"loc,{#10000},0,0,0,0" +locations_default(#10002,#10000,0,0,0,0) +hasLocation(#10000,#10002) +#20000=@"global_scope" +scopes(#20000,0) +#20001=@"script;{#10000},1,1" +#20002=* +lines(#20002,#20001,"interface Foo extends import(""foo"").Bar {}"," +") +#20003=@"loc,{#10000},1,1,1,42" +locations_default(#20003,#10000,1,1,1,42) +hasLocation(#20002,#20003) +numlines(#20001,1,1,0) +#20004=* +tokeninfo(#20004,7,#20001,0,"interface") +#20005=@"loc,{#10000},1,1,1,9" +locations_default(#20005,#10000,1,1,1,9) +hasLocation(#20004,#20005) +#20006=* +tokeninfo(#20006,6,#20001,1,"Foo") +#20007=@"loc,{#10000},1,11,1,13" +locations_default(#20007,#10000,1,11,1,13) +hasLocation(#20006,#20007) +#20008=* +tokeninfo(#20008,7,#20001,2,"extends") +#20009=@"loc,{#10000},1,15,1,21" +locations_default(#20009,#10000,1,15,1,21) +hasLocation(#20008,#20009) +#20010=* +tokeninfo(#20010,7,#20001,3,"import") +#20011=@"loc,{#10000},1,23,1,28" +locations_default(#20011,#10000,1,23,1,28) +hasLocation(#20010,#20011) +#20012=* +tokeninfo(#20012,8,#20001,4,"(") +#20013=@"loc,{#10000},1,29,1,29" +locations_default(#20013,#10000,1,29,1,29) +hasLocation(#20012,#20013) +#20014=* +tokeninfo(#20014,4,#20001,5,"""foo""") +#20015=@"loc,{#10000},1,30,1,34" +locations_default(#20015,#10000,1,30,1,34) +hasLocation(#20014,#20015) +#20016=* +tokeninfo(#20016,8,#20001,6,")") +#20017=@"loc,{#10000},1,35,1,35" +locations_default(#20017,#10000,1,35,1,35) +hasLocation(#20016,#20017) +#20018=* +tokeninfo(#20018,8,#20001,7,".") +#20019=@"loc,{#10000},1,36,1,36" +locations_default(#20019,#10000,1,36,1,36) +hasLocation(#20018,#20019) +#20020=* +tokeninfo(#20020,6,#20001,8,"Bar") +#20021=@"loc,{#10000},1,37,1,39" +locations_default(#20021,#10000,1,37,1,39) +hasLocation(#20020,#20021) +#20022=* +tokeninfo(#20022,8,#20001,9,"{") +#20023=@"loc,{#10000},1,41,1,41" +locations_default(#20023,#10000,1,41,1,41) +hasLocation(#20022,#20023) +#20024=* +tokeninfo(#20024,8,#20001,10,"}") +#20025=@"loc,{#10000},1,42,1,42" +locations_default(#20025,#10000,1,42,1,42) +hasLocation(#20024,#20025) +#20026=* +tokeninfo(#20026,0,#20001,11,"") +#20027=@"loc,{#10000},2,1,2,0" +locations_default(#20027,#10000,2,1,2,0) +hasLocation(#20026,#20027) +toplevels(#20001,0) +#20028=@"loc,{#10000},1,1,2,0" +locations_default(#20028,#10000,1,1,2,0) +hasLocation(#20001,#20028) +#20029=@"local_type_name;{Foo};{#20000}" +local_type_names(#20029,"Foo",#20000) +#20030=* +stmts(#20030,34,#20001,0,"interfa ... .Bar {}") +hasLocation(#20030,#20003) +stmt_containers(#20030,#20001) +#20031=* +typeexprs(#20031,13,#20030,-1,"import(""foo"").Bar") +#20032=@"loc,{#10000},1,23,1,39" +locations_default(#20032,#10000,1,23,1,39) +hasLocation(#20031,#20032) +enclosing_stmt(#20031,#20030) +expr_containers(#20031,#20001) +#20033=* +typeexprs(#20033,31,#20031,0,"import(""foo"")") +#20034=@"loc,{#10000},1,23,1,35" +locations_default(#20034,#10000,1,23,1,35) +hasLocation(#20033,#20034) +enclosing_stmt(#20033,#20030) +expr_containers(#20033,#20001) +#20035=* +exprs(#20035,4,#20033,0,"""foo""") +hasLocation(#20035,#20015) +enclosing_stmt(#20035,#20030) +expr_containers(#20035,#20001) +literals("foo","""foo""",#20035) +#20036=* +regexpterm(#20036,14,#20035,0,"foo") +#20037=@"loc,{#10000},1,31,1,33" +locations_default(#20037,#10000,1,31,1,33) +hasLocation(#20036,#20037) +regexp_const_value(#20036,"foo") +#20038=* +typeexprs(#20038,15,#20031,1,"Bar") +hasLocation(#20038,#20021) +enclosing_stmt(#20038,#20030) +expr_containers(#20038,#20001) +literals("Bar","Bar",#20038) +#20039=* +typeexprs(#20039,1,#20030,0,"Foo") +hasLocation(#20039,#20007) +enclosing_stmt(#20039,#20030) +expr_containers(#20039,#20001) +literals("Foo","Foo",#20039) +typedecl(#20039,#20029) +#20040=* +entry_cfg_node(#20040,#20001) +#20041=@"loc,{#10000},1,1,1,0" +locations_default(#20041,#10000,1,1,1,0) +hasLocation(#20040,#20041) +#20042=* +exit_cfg_node(#20042,#20001) +hasLocation(#20042,#20027) +successor(#20030,#20042) +successor(#20040,#20030) +numlines(#10000,1,1,0) +filetype(#10000,"typescript") diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/ATMConfig.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/ATMConfig.qll index ba555b0de5b..55d75ad2e4d 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/ATMConfig.qll +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/ATMConfig.qll @@ -50,7 +50,8 @@ abstract class AtmConfig extends string { // known sink for the class. exists(EndpointCharacteristic characteristic | characteristic.getEndpoints(sink) and - characteristic.getImplications(this.getASinkEndpointType(), true, 1.0) + characteristic + .getImplications(this.getASinkEndpointType(), true, characteristic.maximalConfidence()) ) } diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll index adc98a5c08c..e1539a504ec 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointCharacteristics.qll @@ -30,18 +30,36 @@ abstract class EndpointCharacteristic extends string { /** * This predicate describes what the characteristic tells us about an endpoint. * - * Params: - * endpointClass: Class 0 is the negative class. Each positive int corresponds to a single sink type. - * isPositiveIndicator: Does this characteristic indicate this endpoint _is_ a member of the class, or that it - * _isn't_ a member of the class? - * confidence: A number in [0, 1], which tells us how strong an indicator this characteristic is for the endpoint - * belonging / not belonging to the given class. + * Params: + * endpointClass: The sink type. Each EndpointType has a predicate getEncoding, which specifies the classifier + * class for this sink type. Class 0 is the negative class (non-sink). Each positive int corresponds to a single + * sink type. + * isPositiveIndicator: If true, this characteristic indicates that this endpoint _is_ a member of the class; if + * false, it indicates that it _isn't_ a member of the class. + * confidence: A float in [0, 1], which tells us how strong an indicator this characteristic is for the endpoint + * belonging / not belonging to the given class. A confidence near zero means this characteristic is a very weak + * indicator of whether or not the endpoint belongs to the class. A confidence of 1 means that all endpoints with + * this characteristic definitively do/don't belong to the class. */ abstract predicate getImplications( EndpointType endpointClass, boolean isPositiveIndicator, float confidence ); + + // The following are some confidence values that are used in practice by the subclasses. They are defined as named + // constants here to make it easier to change them in the future. + final float maximalConfidence() { result = 1.0 } + + final float highConfidence() { result = 0.9 } + + final float mediumConfidence() { result = 0.6 } } +/* + * Characteristics that are indicative of a sink. + * NOTE: Initially each sink type has only one characteristic, which is that it's a sink of this type in the standard + * JavaScript libraries. + */ + /** * Endpoints identified as "DomBasedXssSink" by the standard JavaScript libraries are XSS sinks with maximal confidence. */ @@ -53,7 +71,9 @@ private class DomBasedXssSinkCharacteristic extends EndpointCharacteristic { override predicate getImplications( EndpointType endpointClass, boolean isPositiveIndicator, float confidence ) { - endpointClass instanceof XssSinkType and isPositiveIndicator = true and confidence = 1.0 + endpointClass instanceof XssSinkType and + isPositiveIndicator = true and + confidence = maximalConfidence() } } @@ -69,7 +89,9 @@ private class TaintedPathSinkCharacteristic extends EndpointCharacteristic { override predicate getImplications( EndpointType endpointClass, boolean isPositiveIndicator, float confidence ) { - endpointClass instanceof TaintedPathSinkType and isPositiveIndicator = true and confidence = 1.0 + endpointClass instanceof TaintedPathSinkType and + isPositiveIndicator = true and + confidence = maximalConfidence() } } @@ -87,7 +109,7 @@ private class SqlInjectionSinkCharacteristic extends EndpointCharacteristic { ) { endpointClass instanceof SqlInjectionSinkType and isPositiveIndicator = true and - confidence = 1.0 + confidence = maximalConfidence() } } @@ -105,6 +127,315 @@ private class NosqlInjectionSinkCharacteristic extends EndpointCharacteristic { ) { endpointClass instanceof NosqlInjectionSinkType and isPositiveIndicator = true and - confidence = 1.0 + confidence = maximalConfidence() + } +} + +/* + * Characteristics that are indicative of not being a sink of any type. + */ + +/** + * A characteristic that is an indicator of not being a sink of any type, because it's an argument to a function of a + * builtin object. + */ +abstract private class ArgumentToBuiltinFunctionCharacteristic extends EndpointCharacteristic { + bindingset[this] + ArgumentToBuiltinFunctionCharacteristic() { any() } +} + +/** + * A high-confidence characteristic that indicates that an endpoint is not a sink of any type. + */ +abstract private class NotASinkCharacteristic extends EndpointCharacteristic { + bindingset[this] + NotASinkCharacteristic() { any() } + + override predicate getImplications( + EndpointType endpointClass, boolean isPositiveIndicator, float confidence + ) { + endpointClass instanceof NegativeType and + isPositiveIndicator = true and + confidence = highConfidence() + } +} + +/** + * A medium-confidence characteristic that indicates that an endpoint is not a sink of any type. + * + * TODO: This class is currently not private, because the current extraction logic explicitly avoids including these + * endpoints in the training data. We might want to change this in the future. + */ +abstract class LikelyNotASinkCharacteristic extends EndpointCharacteristic { + bindingset[this] + LikelyNotASinkCharacteristic() { any() } + + override predicate getImplications( + EndpointType endpointClass, boolean isPositiveIndicator, float confidence + ) { + endpointClass instanceof NegativeType and + isPositiveIndicator = true and + confidence = mediumConfidence() + } +} + +private class LodashUnderscore extends NotASinkCharacteristic { + LodashUnderscore() { this = "LodashUnderscoreArgument" } + + override predicate getEndpoints(DataFlow::Node n) { + any(LodashUnderscore::Member m).getACall().getAnArgument() = n + } +} + +private class JQueryArgumentCharacteristic extends NotASinkCharacteristic { + JQueryArgumentCharacteristic() { this = "JQueryArgument" } + + override predicate getEndpoints(DataFlow::Node n) { + any(JQuery::MethodCall m).getAnArgument() = n + } +} + +private class ClientRequestCharacteristic extends NotASinkCharacteristic { + ClientRequestCharacteristic() { this = "ClientRequest" } + + override predicate getEndpoints(DataFlow::Node n) { + exists(ClientRequest r | + r.getAnArgument() = n or n = r.getUrl() or n = r.getHost() or n = r.getADataNode() + ) + } +} + +private class PromiseDefinitionCharacteristic extends NotASinkCharacteristic { + PromiseDefinitionCharacteristic() { this = "PromiseDefinition" } + + override predicate getEndpoints(DataFlow::Node n) { + exists(PromiseDefinition p | + n = [p.getResolveParameter(), p.getRejectParameter()].getACall().getAnArgument() + ) + } +} + +private class CryptographicKeyCharacteristic extends NotASinkCharacteristic { + CryptographicKeyCharacteristic() { this = "CryptographicKey" } + + override predicate getEndpoints(DataFlow::Node n) { n instanceof CryptographicKey } +} + +private class CryptographicOperationFlowCharacteristic extends NotASinkCharacteristic { + CryptographicOperationFlowCharacteristic() { this = "CryptographicOperationFlow" } + + override predicate getEndpoints(DataFlow::Node n) { + any(CryptographicOperation op).getInput() = n + } +} + +private class LoggerMethodCharacteristic extends NotASinkCharacteristic { + LoggerMethodCharacteristic() { this = "LoggerMethod" } + + override predicate getEndpoints(DataFlow::Node n) { + exists(DataFlow::CallNode call | n = call.getAnArgument() | + call.getCalleeName() = getAStandardLoggerMethodName() + ) + } +} + +private class TimeoutCharacteristic extends NotASinkCharacteristic { + TimeoutCharacteristic() { this = "Timeout" } + + override predicate getEndpoints(DataFlow::Node n) { + exists(DataFlow::CallNode call | n = call.getAnArgument() | + call.getCalleeName() = ["setTimeout", "clearTimeout"] + ) + } +} + +private class ReceiverStorageCharacteristic extends NotASinkCharacteristic { + ReceiverStorageCharacteristic() { this = "ReceiverStorage" } + + override predicate getEndpoints(DataFlow::Node n) { + exists(DataFlow::CallNode call | n = call.getAnArgument() | + call.getReceiver() = DataFlow::globalVarRef(["localStorage", "sessionStorage"]) + ) + } +} + +private class StringStartsWithCharacteristic extends NotASinkCharacteristic { + StringStartsWithCharacteristic() { this = "StringStartsWith" } + + override predicate getEndpoints(DataFlow::Node n) { + exists(DataFlow::CallNode call | n = call.getAnArgument() | + call instanceof StringOps::StartsWith + ) + } +} + +private class StringEndsWithCharacteristic extends NotASinkCharacteristic { + StringEndsWithCharacteristic() { this = "StringEndsWith" } + + override predicate getEndpoints(DataFlow::Node n) { + exists(DataFlow::CallNode call | n = call.getAnArgument() | call instanceof StringOps::EndsWith) + } +} + +private class StringRegExpTestCharacteristic extends NotASinkCharacteristic { + StringRegExpTestCharacteristic() { this = "StringRegExpTest" } + + override predicate getEndpoints(DataFlow::Node n) { + exists(DataFlow::CallNode call | n = call.getAnArgument() | + call instanceof StringOps::RegExpTest + ) + } +} + +private class EventRegistrationCharacteristic extends NotASinkCharacteristic { + EventRegistrationCharacteristic() { this = "EventRegistration" } + + override predicate getEndpoints(DataFlow::Node n) { + exists(DataFlow::CallNode call | n = call.getAnArgument() | call instanceof EventRegistration) + } +} + +private class EventDispatchCharacteristic extends NotASinkCharacteristic { + EventDispatchCharacteristic() { this = "EventDispatch" } + + override predicate getEndpoints(DataFlow::Node n) { + exists(DataFlow::CallNode call | n = call.getAnArgument() | call instanceof EventDispatch) + } +} + +private class MembershipCandidateTestCharacteristic extends NotASinkCharacteristic { + MembershipCandidateTestCharacteristic() { this = "MembershipCandidateTest" } + + override predicate getEndpoints(DataFlow::Node n) { + exists(DataFlow::CallNode call | n = call.getAnArgument() | + call = any(MembershipCandidate c).getTest() + ) + } +} + +private class FileSystemAccessCharacteristic extends NotASinkCharacteristic { + FileSystemAccessCharacteristic() { this = "FileSystemAccess" } + + override predicate getEndpoints(DataFlow::Node n) { + exists(DataFlow::CallNode call | n = call.getAnArgument() | call instanceof FileSystemAccess) + } +} + +private class DatabaseAccessCharacteristic extends NotASinkCharacteristic { + DatabaseAccessCharacteristic() { this = "DatabaseAccess" } + + override predicate getEndpoints(DataFlow::Node n) { + // TODO database accesses are less well defined than database query sinks, so this may cover unmodeled sinks on + // existing database models + exists(DataFlow::CallNode call | n = call.getAnArgument() | + [ + call, call.getAMethodCall() + /* command pattern where the query is built, and then exec'ed later */ ] instanceof + DatabaseAccess + ) + } +} + +private class DomCharacteristic extends NotASinkCharacteristic { + DomCharacteristic() { this = "DOM" } + + override predicate getEndpoints(DataFlow::Node n) { + exists(DataFlow::CallNode call | n = call.getAnArgument() | call = DOM::domValueRef()) + } +} + +private class NextFunctionCallCharacteristic extends NotASinkCharacteristic { + NextFunctionCallCharacteristic() { this = "NextFunctionCall" } + + override predicate getEndpoints(DataFlow::Node n) { + exists(DataFlow::CallNode call | n = call.getAnArgument() | + call.getCalleeName() = "next" and + exists(DataFlow::FunctionNode f | call = f.getLastParameter().getACall()) + ) + } +} + +private class DojoRequireCharacteristic extends NotASinkCharacteristic { + DojoRequireCharacteristic() { this = "DojoRequire" } + + override predicate getEndpoints(DataFlow::Node n) { + exists(DataFlow::CallNode call | n = call.getAnArgument() | + call = DataFlow::globalVarRef("dojo").getAPropertyRead("require").getACall() + ) + } +} + +private class Base64ManipulationCharacteristic extends NotASinkCharacteristic { + Base64ManipulationCharacteristic() { this = "Base64Manipulation" } + + override predicate getEndpoints(DataFlow::Node n) { + exists(Base64::Decode d | n = d.getInput()) or + exists(Base64::Encode d | n = d.getInput()) + } +} + +private class ArgumentToArrayCharacteristic extends ArgumentToBuiltinFunctionCharacteristic, + LikelyNotASinkCharacteristic { + ArgumentToArrayCharacteristic() { this = "ArgumentToArray" } + + override predicate getEndpoints(DataFlow::Node n) { + exists(DataFlow::SourceNode builtin, DataFlow::SourceNode receiver, DataFlow::InvokeNode invk | + builtin instanceof DataFlow::ArrayCreationNode + | + receiver = [builtin.getAnInvocation(), builtin] and + invk = [receiver, receiver.getAPropertyRead()].getAnInvocation() and + invk.getAnArgument() = n + ) + } +} + +private class ArgumentToBuiltinGlobalVarRefCharacteristic extends ArgumentToBuiltinFunctionCharacteristic, + LikelyNotASinkCharacteristic { + ArgumentToBuiltinGlobalVarRefCharacteristic() { this = "ArgumentToBuiltinGlobalVarRef" } + + override predicate getEndpoints(DataFlow::Node n) { + exists(DataFlow::SourceNode builtin, DataFlow::SourceNode receiver, DataFlow::InvokeNode invk | + builtin = + DataFlow::globalVarRef([ + "Map", "Set", "WeakMap", "WeakSet", "Number", "Object", "String", "Array", "Error", + "Math", "Boolean" + ]) + | + receiver = [builtin.getAnInvocation(), builtin] and + invk = [receiver, receiver.getAPropertyRead()].getAnInvocation() and + invk.getAnArgument() = n + ) + } +} + +private class ConstantReceiverCharacteristic extends ArgumentToBuiltinFunctionCharacteristic, + NotASinkCharacteristic { + ConstantReceiverCharacteristic() { this = "ConstantReceiver" } + + override predicate getEndpoints(DataFlow::Node n) { + exists(Expr primitive, MethodCallExpr c | + primitive instanceof ConstantString or + primitive instanceof NumberLiteral or + primitive instanceof BooleanLiteral + | + c.calls(primitive, _) and + c.getAnArgument() = n.asExpr() + ) + } +} + +private class BuiltinCallNameCharacteristic extends ArgumentToBuiltinFunctionCharacteristic, + NotASinkCharacteristic { + BuiltinCallNameCharacteristic() { this = "BuiltinCallName" } + + override predicate getEndpoints(DataFlow::Node n) { + exists(DataFlow::CallNode call | + call.getAnArgument() = n and + call.getCalleeName() = + [ + "indexOf", "hasOwnProperty", "substring", "isDecimal", "decode", "encode", "keys", + "shift", "values", "forEach", "toString", "slice", "splice", "push", "isArray", "sort" + ] + ) } } diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointTypes.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointTypes.qll index aa625b12862..d2cc37b1b33 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointTypes.qll +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/EndpointTypes.qll @@ -16,6 +16,11 @@ newtype TEndpointType = abstract class EndpointType extends TEndpointType { abstract string getDescription(); + /** + * Gets the integer representation of this endpoint type. This integer representation specifies the class number + * used by the endpoint scoring model (the classifier) to represent this endpoint type. Class 0 is the negative + * class (non-sink). Each positive int corresponds to a single sink type. + */ abstract int getEncoding(); string toString() { result = getDescription() } diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml b/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml index e0571f38255..fb53f54ded7 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-experimental-atm-lib -version: 0.4.1 +version: 0.4.2 extractor: javascript library: true groups: diff --git a/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml b/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml index cab87ce0e33..725beadcb0e 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml +++ b/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml @@ -1,6 +1,6 @@ name: codeql/javascript-experimental-atm-queries language: javascript -version: 0.4.1 +version: 0.4.2 suites: codeql-suites defaultSuiteFile: codeql-suites/javascript-atm-code-scanning.qls groups: diff --git a/javascript/ql/lib/semmle/javascript/security/internal/SensitiveDataHeuristics.qll b/javascript/ql/lib/semmle/javascript/security/internal/SensitiveDataHeuristics.qll index f49696c5bad..7bc61ee2aee 100644 --- a/javascript/ql/lib/semmle/javascript/security/internal/SensitiveDataHeuristics.qll +++ b/javascript/ql/lib/semmle/javascript/security/internal/SensitiveDataHeuristics.qll @@ -103,7 +103,7 @@ module HeuristicNames { */ string notSensitiveRegexp() { result = - "(?is).*([^\\w$.-]|redact|censor|obfuscate|hash|md5|sha|random|((? 0 and - result.getRegex() = re and - exists(int part_start | - part_start = this.getChild(i - 1).getEnd() + 1 // allow for the | - | - result.getStart() = part_start and - re.alternationOption(start, end, part_start, result.getEnd()) - ) - } - - override string getPrimaryQLClass() { result = "RegExpAlt" } -} - -class RegExpCharEscape = RegExpEscape; - -/** - * An escaped regular expression term, that is, a regular expression - * term starting with a backslash, which is not a backreference. - * - * Example: - * - * ``` - * \. - * \w - * ``` - */ -class RegExpEscape extends RegExpNormalChar { - RegExpEscape() { re.escapedCharacter(start, end) } - +/** An implementation that satisfies the RegexTreeView signature. */ +module Impl implements RegexTreeViewSig { /** - * Gets the name of the escaped; for example, `w` for `\w`. - * TODO: Handle named escapes. + * An element containing a regular expression term, that is, either + * a string literal (parsed as a regular expression) + * or another regular expression term. */ - override string getValue() { - not this.isUnicode() and - this.isIdentityEscape() and - result = this.getUnescaped() - or - this.getUnescaped() = "n" and result = "\n" - or - this.getUnescaped() = "r" and result = "\r" - or - this.getUnescaped() = "t" and result = "\t" - or - this.getUnescaped() = "f" and result = 12.toUnicode() - or - this.getUnescaped() = "v" and result = 11.toUnicode() - or - this.isUnicode() and - result = this.getUnicode() + class RegExpParent extends TRegExpParent { + /** Gets a textual representation of this element. */ + string toString() { result = "RegExpParent" } + + /** Gets the `i`th child term. */ + abstract RegExpTerm getChild(int i); + + /** Gets a child term . */ + RegExpTerm getAChild() { result = this.getChild(_) } + + /** Gets the number of child terms. */ + int getNumChild() { result = count(this.getAChild()) } + + /** Gets the associated regex. */ + abstract Regex getRegex(); } - /** Holds if this terms name is given by the part following the escape character. */ - predicate isIdentityEscape() { not this.getUnescaped() in ["n", "r", "t", "f"] } + /** A string literal used as a regular expression */ + class RegExpLiteral extends TRegExpLiteral, RegExpParent { + Regex re; - override string getPrimaryQLClass() { result = "RegExpEscape" } + RegExpLiteral() { this = TRegExpLiteral(re) } - /** Gets the part of the term following the escape character. That is e.g. "w" if the term is "\w". */ - string getUnescaped() { result = this.getText().suffix(1) } + override RegExpTerm getChild(int i) { i = 0 and result.getRegex() = re and result.isRootTerm() } + + /** Holds if dot, `.`, matches all characters, including newlines. */ + predicate isDotAll() { re.getAMode() = "DOTALL" } + + /** Holds if this regex matching is case-insensitive for this regex. */ + predicate isIgnoreCase() { re.getAMode() = "IGNORECASE" } + + /** Get a string representing all modes for this regex. */ + string getFlags() { result = concat(string mode | mode = re.getAMode() | mode, " | ") } + + override Regex getRegex() { result = re } + + /** Gets the primary QL class for this regex. */ + string getPrimaryQLClass() { result = "RegExpLiteral" } + } /** - * Gets the text for this escape. That is e.g. "\w". + * A regular expression term, that is, a syntactic part of a regular expression. */ - private string getText() { result = re.getText().substring(start, end) } + class RegExpTerm extends RegExpParent { + Regex re; + int start; + int end; - /** - * Holds if this is a unicode escape. - */ - private predicate isUnicode() { this.getText().prefix(2) = ["\\u", "\\U"] } + RegExpTerm() { + this = TRegExpAlt(re, start, end) + or + this = TRegExpBackRef(re, start, end) + or + this = TRegExpCharacterClass(re, start, end) + or + this = TRegExpCharacterRange(re, start, end) + or + this = TRegExpNormalChar(re, start, end) + or + this = TRegExpGroup(re, start, end) + or + this = TRegExpQuantifier(re, start, end) + or + this = TRegExpSequence(re, start, end) + or + this = TRegExpSpecialChar(re, start, end) + } - /** - * Gets the unicode char for this escape. - * E.g. for `\u0061` this returns "a". - */ - private string getUnicode() { - exists(int codepoint | codepoint = sum(this.getHexValueFromUnicode(_)) | - result = codepoint.toUnicode() - ) + /** + * Gets the outermost term of this regular expression. + */ + RegExpTerm getRootTerm() { + this.isRootTerm() and result = this + or + result = this.getParent().(RegExpTerm).getRootTerm() + } + + /** + * Holds if this term is part of a string literal + * that is interpreted as a regular expression. + */ + predicate isUsedAsRegExp() { any() } + + /** + * Holds if this is the root term of a regular expression. + */ + predicate isRootTerm() { start = 0 and end = re.getText().length() } + + override RegExpTerm getChild(int i) { + result = this.(RegExpAlt).getChild(i) + or + result = this.(RegExpBackRef).getChild(i) + or + result = this.(RegExpCharacterClass).getChild(i) + or + result = this.(RegExpCharacterRange).getChild(i) + or + result = this.(RegExpNormalChar).getChild(i) + or + result = this.(RegExpGroup).getChild(i) + or + result = this.(RegExpQuantifier).getChild(i) + or + result = this.(RegExpSequence).getChild(i) + or + result = this.(RegExpSpecialChar).getChild(i) + } + + /** + * Gets the parent term of this regular expression term, or the + * regular expression literal if this is the root term. + */ + RegExpParent getParent() { result.getAChild() = this } + + override Regex getRegex() { result = re } + + /** Gets the offset at which this term starts. */ + int getStart() { result = start } + + /** Gets the offset at which this term ends. */ + int getEnd() { result = end } + + override string toString() { result = re.getText().substring(start, end) } + + /** + * Gets the location of the surrounding regex, as locations inside the regex do not exist. + * To get location information corresponding to the term inside the regex, + * use `hasLocationInfo`. + */ + Location getLocation() { result = re.getLocation() } + + /** Holds if this term is found at the specified location offsets. */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + exists(int re_start, int re_end | + re.getLocation().hasLocationInfo(filepath, startline, re_start, endline, re_end) and + startcolumn = re_start + start + 4 and + endcolumn = re_start + end + 3 + ) + } + + /** Gets the file in which this term is found. */ + File getFile() { result = this.getLocation().getFile() } + + /** Gets the raw source text of this term. */ + string getRawValue() { result = this.toString() } + + /** Gets the string literal in which this term is found. */ + RegExpLiteral getLiteral() { result = TRegExpLiteral(re) } + + /** Gets the regular expression term that is matched (textually) before this one, if any. */ + RegExpTerm getPredecessor() { + exists(RegExpTerm parent | parent = this.getParent() | + result = parent.(RegExpSequence).previousElement(this) + or + not exists(parent.(RegExpSequence).previousElement(this)) and + not parent instanceof RegExpSubPattern and + result = parent.getPredecessor() + ) + } + + /** Gets the regular expression term that is matched (textually) after this one, if any. */ + RegExpTerm getSuccessor() { + exists(RegExpTerm parent | parent = this.getParent() | + result = parent.(RegExpSequence).nextElement(this) + or + not exists(parent.(RegExpSequence).nextElement(this)) and + not parent instanceof RegExpSubPattern and + result = parent.getSuccessor() + ) + } + + /** Gets the primary QL class for this term. */ + string getPrimaryQLClass() { result = "RegExpTerm" } } /** - * Gets int value for the `index`th char in the hex number of the unicode escape. - * E.g. for `\u0061` and `index = 2` this returns 96 (the number `6` interpreted as hex). - */ - private int getHexValueFromUnicode(int index) { - this.isUnicode() and - exists(string hex, string char | hex = this.getText().suffix(2) | - char = hex.charAt(index) and - result = 16.pow(hex.length() - index - 1) * toHex(char) - ) - } -} - -/** - * Gets the hex number for the `hex` char. - */ -private int toHex(string hex) { - hex = [0 .. 9].toString() and - result = hex.toInt() - or - result = 10 and hex = ["a", "A"] - or - result = 11 and hex = ["b", "B"] - or - result = 12 and hex = ["c", "C"] - or - result = 13 and hex = ["d", "D"] - or - result = 14 and hex = ["e", "E"] - or - result = 15 and hex = ["f", "F"] -} - -/** - * A word boundary, that is, a regular expression term of the form `\b`. - */ -class RegExpWordBoundary extends RegExpSpecialChar { - RegExpWordBoundary() { this.getChar() = "\\b" } -} - -/** - * A character class escape in a regular expression. - * That is, an escaped character that denotes multiple characters. - * - * Examples: - * - * ``` - * \w - * \S - * ``` - */ -class RegExpCharacterClassEscape extends RegExpEscape { - RegExpCharacterClassEscape() { this.getValue() in ["d", "D", "s", "S", "w", "W"] } - - override RegExpTerm getChild(int i) { none() } - - override string getPrimaryQLClass() { result = "RegExpCharacterClassEscape" } -} - -/** - * A character class in a regular expression. - * - * Examples: - * - * ``` - * [a-z_] - * [^<>&] - * ``` - */ -class RegExpCharacterClass extends RegExpTerm, TRegExpCharacterClass { - RegExpCharacterClass() { this = TRegExpCharacterClass(re, start, end) } - - /** Holds if this character class is inverted, matching the opposite of its content. */ - predicate isInverted() { re.getChar(start + 1) = "^" } - - /** Gets the `i`th char inside this charater class. */ - string getCharThing(int i) { result = re.getChar(i + start) } - - /** Holds if this character class can match anything. */ - predicate isUniversalClass() { - // [^] - this.isInverted() and not exists(this.getAChild()) - or - // [\w\W] and similar - not this.isInverted() and - exists(string cce1, string cce2 | - cce1 = this.getAChild().(RegExpCharacterClassEscape).getValue() and - cce2 = this.getAChild().(RegExpCharacterClassEscape).getValue() - | - cce1 != cce2 and cce1.toLowerCase() = cce2.toLowerCase() - ) - } - - override RegExpTerm getChild(int i) { - i = 0 and - result.getRegex() = re and - exists(int itemStart, int itemEnd | - result.getStart() = itemStart and - re.char_set_start(start, itemStart) and - re.char_set_child(start, itemStart, itemEnd) and - result.getEnd() = itemEnd - ) - or - i > 0 and - result.getRegex() = re and - exists(int itemStart | itemStart = this.getChild(i - 1).getEnd() | - result.getStart() = itemStart and - re.char_set_child(start, itemStart, result.getEnd()) - ) - } - - override string getPrimaryQLClass() { result = "RegExpCharacterClass" } -} - -/** - * A character range in a character class in a regular expression. - * - * Example: - * - * ``` - * a-z - * ``` - */ -class RegExpCharacterRange extends RegExpTerm, TRegExpCharacterRange { - int lower_end; - int upper_start; - - RegExpCharacterRange() { - this = TRegExpCharacterRange(re, start, end) and - re.charRange(_, start, lower_end, upper_start, end) - } - - /** Holds if this range goes from `lo` to `hi`, in effect is `lo-hi`. */ - predicate isRange(string lo, string hi) { - lo = re.getText().substring(start, lower_end) and - hi = re.getText().substring(upper_start, end) - } - - override RegExpTerm getChild(int i) { - i = 0 and - result.getRegex() = re and - result.getStart() = start and - result.getEnd() = lower_end - or - i = 1 and - result.getRegex() = re and - result.getStart() = upper_start and - result.getEnd() = end - } - - override string getPrimaryQLClass() { result = "RegExpCharacterRange" } -} - -/** - * A normal character in a regular expression, that is, a character - * without special meaning. This includes escaped characters. - * - * Examples: - * ``` - * t - * \t - * ``` - */ -class RegExpNormalChar extends RegExpTerm, TRegExpNormalChar { - RegExpNormalChar() { this = TRegExpNormalChar(re, start, end) } - - /** - * Holds if this constant represents a valid Unicode character (as opposed - * to a surrogate code point that does not correspond to a character by itself.) - */ - predicate isCharacter() { any() } - - /** Gets the string representation of the char matched by this term. */ - string getValue() { result = re.getText().substring(start, end) } - - override RegExpTerm getChild(int i) { none() } - - override string getPrimaryQLClass() { result = "RegExpNormalChar" } -} - -/** - * A constant regular expression term, that is, a regular expression - * term matching a single string. Currently, this will always be a single character. - * - * Example: - * - * ``` - * a - * ``` - */ -class RegExpConstant extends RegExpTerm { - string value; - - RegExpConstant() { - this = TRegExpNormalChar(re, start, end) and - not this instanceof RegExpCharacterClassEscape and - // exclude chars in qualifiers - // TODO: push this into regex library - not exists(int qstart, int qend | re.qualifiedPart(_, qstart, qend, _, _) | - qstart <= start and end <= qend - ) and - value = this.(RegExpNormalChar).getValue() - } - - /** - * Holds if this constant represents a valid Unicode character (as opposed - * to a surrogate code point that does not correspond to a character by itself.) - */ - predicate isCharacter() { any() } - - /** Gets the string matched by this constant term. */ - string getValue() { result = value } - - override RegExpTerm getChild(int i) { none() } - - override string getPrimaryQLClass() { result = "RegExpConstant" } -} - -/** - * A grouped regular expression. - * - * Examples: - * - * ``` - * (ECMA|Java) - * (?:ECMA|Java) - * (?['"]) - * ``` - */ -class RegExpGroup extends RegExpTerm, TRegExpGroup { - RegExpGroup() { this = TRegExpGroup(re, start, end) } - - /** - * Gets the index of this capture group within the enclosing regular - * expression literal. + * A quantified regular expression term. * - * For example, in the regular expression `/((a?).)(?:b)/`, the - * group `((a?).)` has index 1, the group `(a?)` nested inside it - * has index 2, and the group `(?:b)` has no index, since it is - * not a capture group. + * Example: + * + * ``` + * ((ECMA|Java)[sS]cript)* + * ``` */ - int getNumber() { result = re.getGroupNumber(start, end) } + class RegExpQuantifier extends RegExpTerm, TRegExpQuantifier { + int part_end; + boolean may_repeat_forever; - /** Holds if this is a capture group. */ - predicate isCapture() { exists(this.getNumber()) } + RegExpQuantifier() { + this = TRegExpQuantifier(re, start, end) and + re.qualifiedPart(start, part_end, end, _, may_repeat_forever) + } - /** Holds if this is a named capture group. */ - predicate isNamed() { exists(this.getName()) } - - /** Gets the name of this capture group, if any. */ - string getName() { result = re.getGroupName(start, end) } - - override RegExpTerm getChild(int i) { - result.getRegex() = re and - i = 0 and - re.groupContents(start, end, result.getStart(), result.getEnd()) - } - - override string getPrimaryQLClass() { result = "RegExpGroup" } -} - -/** - * A special character in a regular expression. - * - * Examples: - * ``` - * ^ - * $ - * . - * ``` - */ -class RegExpSpecialChar extends RegExpTerm, TRegExpSpecialChar { - string char; - - RegExpSpecialChar() { - this = TRegExpSpecialChar(re, start, end) and - re.specialCharacter(start, end, char) - } - - /** - * Holds if this constant represents a valid Unicode character (as opposed - * to a surrogate code point that does not correspond to a character by itself.) - */ - predicate isCharacter() { any() } - - /** Gets the char for this term. */ - string getChar() { result = char } - - override RegExpTerm getChild(int i) { none() } - - override string getPrimaryQLClass() { result = "RegExpSpecialChar" } -} - -/** - * A dot regular expression. - * - * Example: - * - * ``` - * . - * ``` - */ -class RegExpDot extends RegExpSpecialChar { - RegExpDot() { this.getChar() = "." } - - override string getPrimaryQLClass() { result = "RegExpDot" } -} - -/** - * A dollar assertion `$` or `\Z` matching the end of a line. - * - * Example: - * - * ``` - * $ - * ``` - */ -class RegExpDollar extends RegExpSpecialChar { - RegExpDollar() { this.getChar() = ["$", "\\Z"] } - - override string getPrimaryQLClass() { result = "RegExpDollar" } -} - -/** - * A caret assertion `^` or `\A` matching the beginning of a line. - * - * Example: - * - * ``` - * ^ - * ``` - */ -class RegExpCaret extends RegExpSpecialChar { - RegExpCaret() { this.getChar() = ["^", "\\A"] } - - override string getPrimaryQLClass() { result = "RegExpCaret" } -} - -/** - * A zero-width match, that is, either an empty group or an assertion. - * - * Examples: - * ``` - * () - * (?=\w) - * ``` - */ -class RegExpZeroWidthMatch extends RegExpGroup { - RegExpZeroWidthMatch() { re.zeroWidthMatch(start, end) } - - override RegExpTerm getChild(int i) { none() } - - override string getPrimaryQLClass() { result = "RegExpZeroWidthMatch" } -} - -/** - * A zero-width lookahead or lookbehind assertion. - * - * Examples: - * - * ``` - * (?=\w) - * (?!\n) - * (?<=\.) - * (?` - * in a regular expression. - * - * Examples: - * - * ``` - * \1 - * (?P=quote) - * ``` - */ -class RegExpBackRef extends RegExpTerm, TRegExpBackRef { - RegExpBackRef() { this = TRegExpBackRef(re, start, end) } /** - * Gets the number of the capture group this back reference refers to, if any. + * A regular expression term that permits unlimited repetitions. */ - int getNumber() { result = re.getBackrefNumber(start, end) } + class InfiniteRepetitionQuantifier extends RegExpQuantifier { + InfiniteRepetitionQuantifier() { this.mayRepeatForever() } + } /** - * Gets the name of the capture group this back reference refers to, if any. + * A star-quantified term. + * + * Example: + * + * ``` + * \w* + * ``` */ - string getName() { result = re.getBackrefName(start, end) } + class RegExpStar extends InfiniteRepetitionQuantifier { + RegExpStar() { this.getQualifier().charAt(0) = "*" } - /** Gets the capture group this back reference refers to. */ - RegExpGroup getGroup() { - this.hasLiteralAndNumber(result.getLiteral(), result.getNumber()) or - this.hasLiteralAndName(result.getLiteral(), result.getName()) + override string getPrimaryQLClass() { result = "RegExpStar" } } - /** Join-order helper for `getGroup`. */ - pragma[nomagic] - private predicate hasLiteralAndNumber(RegExpLiteral literal, int number) { - literal = this.getLiteral() and - number = this.getNumber() + /** + * A plus-quantified term. + * + * Example: + * + * ``` + * \w+ + * ``` + */ + class RegExpPlus extends InfiniteRepetitionQuantifier { + RegExpPlus() { this.getQualifier().charAt(0) = "+" } + + override string getPrimaryQLClass() { result = "RegExpPlus" } } - /** Join-order helper for `getGroup`. */ - pragma[nomagic] - private predicate hasLiteralAndName(RegExpLiteral literal, string name) { - literal = this.getLiteral() and - name = this.getName() + /** + * An optional term. + * + * Example: + * + * ``` + * ;? + * ``` + */ + class RegExpOpt extends RegExpQuantifier { + RegExpOpt() { this.getQualifier().charAt(0) = "?" } + + override string getPrimaryQLClass() { result = "RegExpOpt" } } - override RegExpTerm getChild(int i) { none() } + /** + * A range-quantified term + * + * Examples: + * + * ``` + * \w{2,4} + * \w{2,} + * \w{2} + * ``` + */ + class RegExpRange extends RegExpQuantifier { + string upper; + string lower; - override string getPrimaryQLClass() { result = "RegExpBackRef" } + RegExpRange() { re.multiples(part_end, end, lower, upper) } + + /** Gets the string defining the upper bound of this range, if any. */ + string getUpper() { result = upper } + + /** Gets the string defining the lower bound of this range, if any. */ + string getLower() { result = lower } + + /** + * Gets the upper bound of the range, if any. + * + * If there is no upper bound, any number of repetitions is allowed. + * For a term of the form `r{lo}`, both the lower and the upper bound + * are `lo`. + */ + int getUpperBound() { result = this.getUpper().toInt() } + + /** Gets the lower bound of the range. */ + int getLowerBound() { result = this.getLower().toInt() } + + override string getPrimaryQLClass() { result = "RegExpRange" } + } + + /** + * A sequence term. + * + * Example: + * + * ``` + * (ECMA|Java)Script + * ``` + * + * This is a sequence with the elements `(ECMA|Java)` and `Script`. + */ + class RegExpSequence extends RegExpTerm, TRegExpSequence { + RegExpSequence() { this = TRegExpSequence(re, start, end) } + + override RegExpTerm getChild(int i) { result = seqChild(re, start, end, i) } + + /** Gets the element preceding `element` in this sequence. */ + RegExpTerm previousElement(RegExpTerm element) { element = this.nextElement(result) } + + /** Gets the element following `element` in this sequence. */ + RegExpTerm nextElement(RegExpTerm element) { + exists(int i | + element = this.getChild(i) and + result = this.getChild(i + 1) + ) + } + + override string getPrimaryQLClass() { result = "RegExpSequence" } + } + + /** + * An alternative term, that is, a term of the form `a|b`. + * + * Example: + * + * ``` + * ECMA|Java + * ``` + */ + class RegExpAlt extends RegExpTerm, TRegExpAlt { + RegExpAlt() { this = TRegExpAlt(re, start, end) } + + override RegExpTerm getChild(int i) { + i = 0 and + result.getRegex() = re and + result.getStart() = start and + exists(int part_end | + re.alternationOption(start, end, start, part_end) and + result.getEnd() = part_end + ) + or + i > 0 and + result.getRegex() = re and + exists(int part_start | + part_start = this.getChild(i - 1).getEnd() + 1 // allow for the | + | + result.getStart() = part_start and + re.alternationOption(start, end, part_start, result.getEnd()) + ) + } + + override string getPrimaryQLClass() { result = "RegExpAlt" } + } + + class RegExpCharEscape = RegExpEscape; + + /** + * An escaped regular expression term, that is, a regular expression + * term starting with a backslash, which is not a backreference. + * + * Example: + * + * ``` + * \. + * \w + * ``` + */ + class RegExpEscape extends RegExpNormalChar { + RegExpEscape() { re.escapedCharacter(start, end) } + + /** + * Gets the name of the escaped; for example, `w` for `\w`. + * TODO: Handle named escapes. + */ + override string getValue() { + not this.isUnicode() and + this.isIdentityEscape() and + result = this.getUnescaped() + or + this.getUnescaped() = "n" and result = "\n" + or + this.getUnescaped() = "r" and result = "\r" + or + this.getUnescaped() = "t" and result = "\t" + or + this.getUnescaped() = "f" and result = 12.toUnicode() + or + this.getUnescaped() = "v" and result = 11.toUnicode() + or + this.isUnicode() and + result = this.getUnicode() + } + + /** Holds if this terms name is given by the part following the escape character. */ + predicate isIdentityEscape() { not this.getUnescaped() in ["n", "r", "t", "f"] } + + override string getPrimaryQLClass() { result = "RegExpEscape" } + + /** Gets the part of the term following the escape character. That is e.g. "w" if the term is "\w". */ + string getUnescaped() { result = this.getText().suffix(1) } + + /** + * Gets the text for this escape. That is e.g. "\w". + */ + private string getText() { result = re.getText().substring(start, end) } + + /** + * Holds if this is a unicode escape. + */ + private predicate isUnicode() { this.getText().prefix(2) = ["\\u", "\\U"] } + + /** + * Gets the unicode char for this escape. + * E.g. for `\u0061` this returns "a". + */ + private string getUnicode() { + exists(int codepoint | codepoint = sum(this.getHexValueFromUnicode(_)) | + result = codepoint.toUnicode() + ) + } + + /** + * Gets int value for the `index`th char in the hex number of the unicode escape. + * E.g. for `\u0061` and `index = 2` this returns 96 (the number `6` interpreted as hex). + */ + private int getHexValueFromUnicode(int index) { + this.isUnicode() and + exists(string hex, string char | hex = this.getText().suffix(2) | + char = hex.charAt(index) and + result = 16.pow(hex.length() - index - 1) * toHex(char) + ) + } + } + + /** + * Gets the hex number for the `hex` char. + */ + private int toHex(string hex) { + hex = [0 .. 9].toString() and + result = hex.toInt() + or + result = 10 and hex = ["a", "A"] + or + result = 11 and hex = ["b", "B"] + or + result = 12 and hex = ["c", "C"] + or + result = 13 and hex = ["d", "D"] + or + result = 14 and hex = ["e", "E"] + or + result = 15 and hex = ["f", "F"] + } + + /** + * A word boundary, that is, a regular expression term of the form `\b`. + */ + class RegExpWordBoundary extends RegExpSpecialChar { + RegExpWordBoundary() { this.getChar() = "\\b" } + } + + /** + * A character class escape in a regular expression. + * That is, an escaped character that denotes multiple characters. + * + * Examples: + * + * ``` + * \w + * \S + * ``` + */ + class RegExpCharacterClassEscape extends RegExpEscape { + RegExpCharacterClassEscape() { this.getValue() in ["d", "D", "s", "S", "w", "W"] } + + override RegExpTerm getChild(int i) { none() } + + override string getPrimaryQLClass() { result = "RegExpCharacterClassEscape" } + } + + /** + * A character class in a regular expression. + * + * Examples: + * + * ``` + * [a-z_] + * [^<>&] + * ``` + */ + class RegExpCharacterClass extends RegExpTerm, TRegExpCharacterClass { + RegExpCharacterClass() { this = TRegExpCharacterClass(re, start, end) } + + /** Holds if this character class is inverted, matching the opposite of its content. */ + predicate isInverted() { re.getChar(start + 1) = "^" } + + /** Gets the `i`th char inside this charater class. */ + string getCharThing(int i) { result = re.getChar(i + start) } + + /** Holds if this character class can match anything. */ + predicate isUniversalClass() { + // [^] + this.isInverted() and not exists(this.getAChild()) + or + // [\w\W] and similar + not this.isInverted() and + exists(string cce1, string cce2 | + cce1 = this.getAChild().(RegExpCharacterClassEscape).getValue() and + cce2 = this.getAChild().(RegExpCharacterClassEscape).getValue() + | + cce1 != cce2 and cce1.toLowerCase() = cce2.toLowerCase() + ) + } + + override RegExpTerm getChild(int i) { + i = 0 and + result.getRegex() = re and + exists(int itemStart, int itemEnd | + result.getStart() = itemStart and + re.char_set_start(start, itemStart) and + re.char_set_child(start, itemStart, itemEnd) and + result.getEnd() = itemEnd + ) + or + i > 0 and + result.getRegex() = re and + exists(int itemStart | itemStart = this.getChild(i - 1).getEnd() | + result.getStart() = itemStart and + re.char_set_child(start, itemStart, result.getEnd()) + ) + } + + override string getPrimaryQLClass() { result = "RegExpCharacterClass" } + } + + /** + * A character range in a character class in a regular expression. + * + * Example: + * + * ``` + * a-z + * ``` + */ + class RegExpCharacterRange extends RegExpTerm, TRegExpCharacterRange { + int lower_end; + int upper_start; + + RegExpCharacterRange() { + this = TRegExpCharacterRange(re, start, end) and + re.charRange(_, start, lower_end, upper_start, end) + } + + /** Holds if this range goes from `lo` to `hi`, in effect is `lo-hi`. */ + predicate isRange(string lo, string hi) { + lo = re.getText().substring(start, lower_end) and + hi = re.getText().substring(upper_start, end) + } + + override RegExpTerm getChild(int i) { + i = 0 and + result.getRegex() = re and + result.getStart() = start and + result.getEnd() = lower_end + or + i = 1 and + result.getRegex() = re and + result.getStart() = upper_start and + result.getEnd() = end + } + + override string getPrimaryQLClass() { result = "RegExpCharacterRange" } + } + + /** + * A normal character in a regular expression, that is, a character + * without special meaning. This includes escaped characters. + * + * Examples: + * ``` + * t + * \t + * ``` + */ + class RegExpNormalChar extends RegExpTerm, TRegExpNormalChar { + RegExpNormalChar() { this = TRegExpNormalChar(re, start, end) } + + /** + * Holds if this constant represents a valid Unicode character (as opposed + * to a surrogate code point that does not correspond to a character by itself.) + */ + predicate isCharacter() { any() } + + /** Gets the string representation of the char matched by this term. */ + string getValue() { result = re.getText().substring(start, end) } + + override RegExpTerm getChild(int i) { none() } + + override string getPrimaryQLClass() { result = "RegExpNormalChar" } + } + + /** + * A constant regular expression term, that is, a regular expression + * term matching a single string. Currently, this will always be a single character. + * + * Example: + * + * ``` + * a + * ``` + */ + class RegExpConstant extends RegExpTerm { + string value; + + RegExpConstant() { + this = TRegExpNormalChar(re, start, end) and + not this instanceof RegExpCharacterClassEscape and + // exclude chars in qualifiers + // TODO: push this into regex library + not exists(int qstart, int qend | re.qualifiedPart(_, qstart, qend, _, _) | + qstart <= start and end <= qend + ) and + value = this.(RegExpNormalChar).getValue() + } + + /** + * Holds if this constant represents a valid Unicode character (as opposed + * to a surrogate code point that does not correspond to a character by itself.) + */ + predicate isCharacter() { any() } + + /** Gets the string matched by this constant term. */ + string getValue() { result = value } + + override RegExpTerm getChild(int i) { none() } + + override string getPrimaryQLClass() { result = "RegExpConstant" } + } + + /** + * A grouped regular expression. + * + * Examples: + * + * ``` + * (ECMA|Java) + * (?:ECMA|Java) + * (?['"]) + * ``` + */ + class RegExpGroup extends RegExpTerm, TRegExpGroup { + RegExpGroup() { this = TRegExpGroup(re, start, end) } + + /** + * Gets the index of this capture group within the enclosing regular + * expression literal. + * + * For example, in the regular expression `/((a?).)(?:b)/`, the + * group `((a?).)` has index 1, the group `(a?)` nested inside it + * has index 2, and the group `(?:b)` has no index, since it is + * not a capture group. + */ + int getNumber() { result = re.getGroupNumber(start, end) } + + /** Holds if this is a capture group. */ + predicate isCapture() { exists(this.getNumber()) } + + /** Holds if this is a named capture group. */ + predicate isNamed() { exists(this.getName()) } + + /** Gets the name of this capture group, if any. */ + string getName() { result = re.getGroupName(start, end) } + + override RegExpTerm getChild(int i) { + result.getRegex() = re and + i = 0 and + re.groupContents(start, end, result.getStart(), result.getEnd()) + } + + override string getPrimaryQLClass() { result = "RegExpGroup" } + } + + /** + * A special character in a regular expression. + * + * Examples: + * ``` + * ^ + * $ + * . + * ``` + */ + class RegExpSpecialChar extends RegExpTerm, TRegExpSpecialChar { + string char; + + RegExpSpecialChar() { + this = TRegExpSpecialChar(re, start, end) and + re.specialCharacter(start, end, char) + } + + /** + * Holds if this constant represents a valid Unicode character (as opposed + * to a surrogate code point that does not correspond to a character by itself.) + */ + predicate isCharacter() { any() } + + /** Gets the char for this term. */ + string getChar() { result = char } + + override RegExpTerm getChild(int i) { none() } + + override string getPrimaryQLClass() { result = "RegExpSpecialChar" } + } + + /** + * A dot regular expression. + * + * Example: + * + * ``` + * . + * ``` + */ + class RegExpDot extends RegExpSpecialChar { + RegExpDot() { this.getChar() = "." } + + override string getPrimaryQLClass() { result = "RegExpDot" } + } + + /** + * A dollar assertion `$` or `\Z` matching the end of a line. + * + * Example: + * + * ``` + * $ + * ``` + */ + class RegExpDollar extends RegExpSpecialChar { + RegExpDollar() { this.getChar() = ["$", "\\Z"] } + + override string getPrimaryQLClass() { result = "RegExpDollar" } + } + + /** + * A caret assertion `^` or `\A` matching the beginning of a line. + * + * Example: + * + * ``` + * ^ + * ``` + */ + class RegExpCaret extends RegExpSpecialChar { + RegExpCaret() { this.getChar() = ["^", "\\A"] } + + override string getPrimaryQLClass() { result = "RegExpCaret" } + } + + /** + * A zero-width match, that is, either an empty group or an assertion. + * + * Examples: + * ``` + * () + * (?=\w) + * ``` + */ + class RegExpZeroWidthMatch extends RegExpGroup { + RegExpZeroWidthMatch() { re.zeroWidthMatch(start, end) } + + override RegExpTerm getChild(int i) { none() } + + override string getPrimaryQLClass() { result = "RegExpZeroWidthMatch" } + } + + /** + * A zero-width lookahead or lookbehind assertion. + * + * Examples: + * + * ``` + * (?=\w) + * (?!\n) + * (?<=\.) + * (?` + * in a regular expression. + * + * Examples: + * + * ``` + * \1 + * (?P=quote) + * ``` + */ + class RegExpBackRef extends RegExpTerm, TRegExpBackRef { + RegExpBackRef() { this = TRegExpBackRef(re, start, end) } + + /** + * Gets the number of the capture group this back reference refers to, if any. + */ + int getNumber() { result = re.getBackrefNumber(start, end) } + + /** + * Gets the name of the capture group this back reference refers to, if any. + */ + string getName() { result = re.getBackrefName(start, end) } + + /** Gets the capture group this back reference refers to. */ + RegExpGroup getGroup() { + this.hasLiteralAndNumber(result.getLiteral(), result.getNumber()) or + this.hasLiteralAndName(result.getLiteral(), result.getName()) + } + + /** Join-order helper for `getGroup`. */ + pragma[nomagic] + private predicate hasLiteralAndNumber(RegExpLiteral literal, int number) { + literal = this.getLiteral() and + number = this.getNumber() + } + + /** Join-order helper for `getGroup`. */ + pragma[nomagic] + private predicate hasLiteralAndName(RegExpLiteral literal, string name) { + literal = this.getLiteral() and + name = this.getName() + } + + override RegExpTerm getChild(int i) { none() } + + override string getPrimaryQLClass() { result = "RegExpBackRef" } + } + + class Top = RegExpParent; + + /** + * Holds if `term` is an escape class representing e.g. `\d`. + * `clazz` is which character class it represents, e.g. "d" for `\d`. + */ + predicate isEscapeClass(RegExpTerm term, string clazz) { + exists(RegExpCharacterClassEscape escape | term = escape | escape.getValue() = clazz) + } + + /** + * Holds if `term` is a possessive quantifier. + * As python's regexes do not support possessive quantifiers, this never holds, but is used by the shared library. + */ + predicate isPossessive(RegExpQuantifier term) { none() } + + /** + * Holds if the regex that `term` is part of is used in a way that ignores any leading prefix of the input it's matched against. + * Not yet implemented for Python. + */ + predicate matchesAnyPrefix(RegExpTerm term) { any() } + + /** + * Holds if the regex that `term` is part of is used in a way that ignores any trailing suffix of the input it's matched against. + * Not yet implemented for Python. + */ + predicate matchesAnySuffix(RegExpTerm term) { any() } + + /** + * Holds if the regular expression should not be considered. + * + * We make the pragmatic performance optimization to ignore regular expressions in files + * that does not belong to the project code (such as installed dependencies). + */ + predicate isExcluded(RegExpParent parent) { + not exists(parent.getRegex().getLocation().getFile().getRelativePath()) + or + // Regexes with many occurrences of ".*" may cause the polynomial ReDoS computation to explode, so + // we explicitly exclude these. + count(int i | exists(parent.getRegex().getText().regexpFind("\\.\\*", i, _)) | i) > 10 + } + + /** + * Holds if `root` has the `i` flag for case-insensitive matching. + */ + predicate isIgnoreCase(RegExpTerm root) { + root.isRootTerm() and + root.getLiteral().isIgnoreCase() + } + + /** + * Holds if `root` has the `s` flag for multi-line matching. + */ + predicate isDotAll(RegExpTerm root) { + root.isRootTerm() and + root.getLiteral().isDotAll() + } } - -/** Gets the parse tree resulting from parsing `re`, if such has been constructed. */ -RegExpTerm getParsedRegExp(StrConst re) { result.getRegex() = re and result.isRootTerm() } diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll index dde16ab5a2a..f681e90aa21 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll @@ -136,6 +136,18 @@ module Consistency { msg = "Local flow step does not preserve enclosing callable." } + query predicate readStepIsLocal(Node n1, Node n2, string msg) { + readStep(n1, _, n2) and + nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and + msg = "Read step does not preserve enclosing callable." + } + + query predicate storeStepIsLocal(Node n1, Node n2, string msg) { + storeStep(n1, _, n2) and + nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and + msg = "Store step does not preserve enclosing callable." + } + private DataFlowType typeRepr() { result = getNodeType(_) } query predicate compatibleTypesReflexive(DataFlowType t, string msg) { diff --git a/python/ql/lib/semmle/python/security/BadTagFilterQuery.qll b/python/ql/lib/semmle/python/security/BadTagFilterQuery.qll index 95bfbeeeb5d..446e4487a0f 100644 --- a/python/ql/lib/semmle/python/security/BadTagFilterQuery.qll +++ b/python/ql/lib/semmle/python/security/BadTagFilterQuery.qll @@ -2,155 +2,7 @@ * Provides predicates for reasoning about bad tag filter vulnerabilities. */ -import regexp.RegexpMatching - -/** - * Holds if the regexp `root` should be tested against `str`. - * Implements the `isRegexpMatchingCandidateSig` signature from `RegexpMatching`. - * `ignorePrefix` toggles whether the regular expression should be treated as accepting any prefix if it's unanchored. - * `testWithGroups` toggles whether it's tested which groups are filled by a given input string. - */ -private predicate isBadTagFilterCandidate( - RootTerm root, string str, boolean ignorePrefix, boolean testWithGroups -) { - // the regexp must mention "<" and ">" explicitly. - forall(string angleBracket | angleBracket = ["<", ">"] | - any(RegExpConstant term | term.getValue().matches("%" + angleBracket + "%")).getRootTerm() = - root - ) and - ignorePrefix = true and - ( - str = ["", "", "", "", "", - "", "", "", "", - "", "", - "", "", "", - "", "", "", - "", "") and - regexp.matches("") and - not regexp.matches("") and - ( - not regexp.matches("") and - msg = "This regular expression matches , but not " - or - not regexp.matches("") and - msg = "This regular expression matches , but not " - ) - or - regexp.matches("") and - regexp.matches("") and - not regexp.matches("") and - not regexp.matches("") and - msg = "This regular expression does not match script tags where the attribute uses single-quotes." - or - regexp.matches("") and - regexp.matches("") and - not regexp.matches("") and - not regexp.matches("") and - msg = "This regular expression does not match script tags where the attribute uses double-quotes." - or - regexp.matches("") and - regexp.matches("") and - not regexp.matches("") and - not regexp.matches("") and - not regexp.matches("") and - msg = "This regular expression does not match script tags where tabs are used between attributes." - or - regexp.matches("") and - not RegExpFlags::isIgnoreCase(regexp) and - not regexp.matches("") and - not regexp.matches("") and - ( - not regexp.matches("") and - msg = "This regular expression does not match upper case ") and - regexp.matches("") and - msg = "This regular expression does not match mixed case ") and - not regexp.matches("") and - not regexp.matches("") and - ( - not regexp.matches("") and - msg = "This regular expression does not match script end tags like ." - or - not regexp.matches("") and - msg = "This regular expression does not match script end tags like ." - or - not regexp.matches("", + "", "", "", "", + "", "", + "", "", "", + "", "", + "", "", + "") and + regexp.matches("") and + not regexp.matches("") and + ( + not regexp.matches("") and + msg = "This regular expression matches , but not " + or + not regexp.matches("") and + msg = "This regular expression matches , but not " + ) + or + regexp.matches("") and + regexp.matches("") and + not regexp.matches("") and + not regexp.matches("") and + msg = + "This regular expression does not match script tags where the attribute uses single-quotes." + or + regexp.matches("") and + regexp.matches("") and + not regexp.matches("") and + not regexp.matches("") and + msg = + "This regular expression does not match script tags where the attribute uses double-quotes." + or + regexp.matches("") and + regexp.matches("") and + not regexp.matches("") and + not regexp.matches("") and + not regexp.matches("") and + msg = + "This regular expression does not match script tags where tabs are used between attributes." + or + regexp.matches("") and + not isIgnoreCase(regexp) and + not regexp.matches("") and + not regexp.matches("") and + ( + not regexp.matches("") and + msg = "This regular expression does not match upper case ") and + regexp.matches("") and + msg = "This regular expression does not match mixed case ") and + not regexp.matches("") and + not regexp.matches("") and + ( + not regexp.matches("") and + msg = "This regular expression does not match script end tags like ." + or + not regexp.matches("") and + msg = "This regular expression does not match script end tags like ." + or + not regexp.matches("